Skip to content
Snippets Groups Projects
app_adsiprog.c 46.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • Mark Spencer's avatar
    Mark Spencer committed
    					return -1;
    				}
    			}
    			/* Separate commands with 0xff */
    			sub->data[sub->datalen] = 0xff;
    			sub->datalen++;
    			sub->inscount++;
    			return 0;
    		}
    	}
    	return -1;
    }
    
    
    static int adsi_process(struct adsi_script *state, char *buf, const char *script, int lineno)
    
    	char *keyword = get_token(&buf, script, lineno);
    	char *args, vname[256], tmp[80], tmp2[80];
    	int lrci, wi, event;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct adsi_display *disp;
    	struct adsi_subscript *newsub;
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    	if (!keyword)
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	switch(state->state) {
    	case STATE_NORMAL:
    		if (!strcasecmp(keyword, "DESCRIPTION")) {
    
    			if ((args = get_token(&buf, script, lineno))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				if (process_token(state->desc, args, sizeof(state->desc) - 1, ARG_STRING))
    					ast_log(LOG_WARNING, "'%s' is not a valid token for DESCRIPTION at line %d of %s\n", args, lineno, script);
    			} else
    				ast_log(LOG_WARNING, "Missing argument for DESCRIPTION at line %d of %s\n", lineno, script);
    		} else if (!strcasecmp(keyword, "VERSION")) {
    
    			if ((args = get_token(&buf, script, lineno))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				if (process_token(&state->ver, args, sizeof(state->ver) - 1, ARG_NUMBER))
    					ast_log(LOG_WARNING, "'%s' is not a valid token for VERSION at line %d of %s\n", args, lineno, script);
    			} else
    				ast_log(LOG_WARNING, "Missing argument for VERSION at line %d of %s\n", lineno, script);
    		} else if (!strcasecmp(keyword, "SECURITY")) {
    
    			if ((args = get_token(&buf, script, lineno))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				if (process_token(state->sec, args, sizeof(state->sec) - 1, ARG_STRING | ARG_NUMBER))
    					ast_log(LOG_WARNING, "'%s' is not a valid token for SECURITY at line %d of %s\n", args, lineno, script);
    			} else
    				ast_log(LOG_WARNING, "Missing argument for SECURITY at line %d of %s\n", lineno, script);
    		} else if (!strcasecmp(keyword, "FDN")) {
    
    			if ((args = get_token(&buf, script, lineno))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				if (process_token(state->fdn, args, sizeof(state->fdn) - 1, ARG_STRING | ARG_NUMBER))
    					ast_log(LOG_WARNING, "'%s' is not a valid token for FDN at line %d of %s\n", args, lineno, script);
    			} else
    				ast_log(LOG_WARNING, "Missing argument for FDN at line %d of %s\n", lineno, script);
    		} else if (!strcasecmp(keyword, "KEY")) {
    
    			if (!(args = get_token(&buf, script, lineno))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_log(LOG_WARNING, "KEY definition missing name at line %d of %s\n", lineno, script);
    				break;
    			}
    			if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
    
    				ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY name at line %d of %s\n", args, lineno, script);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				break;
    			}
    
    			if (!(state->key = getkeybyname(state, vname, script, lineno))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_log(LOG_WARNING, "Out of key space at line %d of %s\n", lineno, script);
    				break;
    			}
    			if (state->key->defined) {
    				ast_log(LOG_WARNING, "Cannot redefine key '%s' at line %d of %s\n", vname, lineno, script);
    				break;
    			}
    
    			if (!(args = get_token(&buf, script, lineno)) || strcasecmp(args, "IS")) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_log(LOG_WARNING, "Expecting 'IS', but got '%s' at line %d of %s\n", args ? args : "<nothing>", lineno, script);
    				break;
    			}
    
    			if (!(args = get_token(&buf, script, lineno))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_log(LOG_WARNING, "KEY definition missing short name at line %d of %s\n", lineno, script);
    				break;
    			}
    			if (process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
    
    				ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY short name at line %d of %s\n", args, lineno, script);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				break;
    			}
    
    			if ((args = get_token(&buf, script, lineno))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				if (strcasecmp(args, "OR")) {
    					ast_log(LOG_WARNING, "Expecting 'OR' but got '%s' instead at line %d of %s\n", args, lineno, script);
    					break;
    				}
    
    				if (!(args = get_token(&buf, script, lineno))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    					ast_log(LOG_WARNING, "KEY definition missing optional long name at line %d of %s\n", lineno, script);
    					break;
    				}
    				if (process_token(tmp2, args, sizeof(tmp2) - 1, ARG_STRING)) {
    
    					ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY long name at line %d of %s\n", args, lineno, script);
    
    Mark Spencer's avatar
    Mark Spencer committed
    					break;
    				}
    			} else {
    
    				ast_copy_string(tmp2, tmp, sizeof(tmp2));
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    			if (strlen(tmp2) > 18) {
    				ast_log(LOG_WARNING, "Truncating full name to 18 characters at line %d of %s\n", lineno, script);
    				tmp2[18] = '\0';
    			}
    			if (strlen(tmp) > 7) {
    				ast_log(LOG_WARNING, "Truncating short name to 7 bytes at line %d of %s\n", lineno, script);
    				tmp[7] = '\0';
    			}
    			/* Setup initial stuff */
    			state->key->retstr[0] = 128;
    			/* 1 has the length */
    			state->key->retstr[2] = state->key->id;
    			/* Put the Full name in */
    			memcpy(state->key->retstr + 3, tmp2, strlen(tmp2));
    			/* Update length */
    			state->key->retstrlen = strlen(tmp2) + 3;
    			/* Put trailing 0xff */
    			state->key->retstr[state->key->retstrlen++] = 0xff;
    			/* Put the short name */
    			memcpy(state->key->retstr + state->key->retstrlen, tmp, strlen(tmp));
    			/* Update length */
    			state->key->retstrlen += strlen(tmp);
    			/* Put trailing 0xff */
    			state->key->retstr[state->key->retstrlen++] = 0xff;
    			/* Record initial length */
    			state->key->initlen = state->key->retstrlen;
    			state->state = STATE_INKEY;
    		} else if (!strcasecmp(keyword, "SUB")) {
    
    			if (!(args = get_token(&buf, script, lineno))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_log(LOG_WARNING, "SUB definition missing name at line %d of %s\n", lineno, script);
    				break;
    			}
    			if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
    
    				ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY name at line %d of %s\n", args, lineno, script);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				break;
    			}
    
    			if (!(state->sub = getsubbyname(state, vname, script, lineno))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_log(LOG_WARNING, "Out of subroutine space at line %d of %s\n", lineno, script);
    				break;
    			}
    			if (state->sub->defined) {
    				ast_log(LOG_WARNING, "Cannot redefine subroutine '%s' at line %d of %s\n", vname, lineno, script);
    				break;
    			}
    			/* Setup sub */
    			state->sub->data[0] = 130;
    			/* 1 is the length */
    			state->sub->data[2] = 0x0; /* Clear extensibility bit */
    			state->sub->datalen = 3;
    			if (state->sub->id) {
    				/* If this isn't the main subroutine, make a subroutine label for it */
    				state->sub->data[3] = 9;
    				state->sub->data[4] = state->sub->id;
    				/* 5 is length */
    				state->sub->data[6] = 0xff;
    				state->sub->datalen = 7;
    			}
    
    			if (!(args = get_token(&buf, script, lineno)) || strcasecmp(args, "IS")) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_log(LOG_WARNING, "Expecting 'IS', but got '%s' at line %d of %s\n", args ? args : "<nothing>", lineno, script);
    				break;
    			}
    			state->state = STATE_INSUB;
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    		} else if (!strcasecmp(keyword, "STATE")) {
    
    			if (!(args = get_token(&buf, script, lineno))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_log(LOG_WARNING, "STATE definition missing name at line %d of %s\n", lineno, script);
    				break;
    			}
    			if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
    
    				ast_log(LOG_WARNING, "'%s' is not a valid token for a STATE name at line %d of %s\n", args, lineno, script);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				break;
    			}
    			if (getstatebyname(state, vname, script, lineno, 0)) {
    				ast_log(LOG_WARNING, "State '%s' is already defined at line %d of %s\n", vname, lineno, script);
    				break;
    			}
    			getstatebyname(state, vname, script, lineno, 1);
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    		} else if (!strcasecmp(keyword, "FLAG")) {
    
    			if (!(args = get_token(&buf, script, lineno))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_log(LOG_WARNING, "FLAG definition missing name at line %d of %s\n", lineno, script);
    				break;
    			}
    			if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
    
    				ast_log(LOG_WARNING, "'%s' is not a valid token for a FLAG name at line %d of %s\n", args, lineno, script);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				break;
    			}
    			if (getflagbyname(state, vname, script, lineno, 0)) {
    				ast_log(LOG_WARNING, "Flag '%s' is already defined\n", vname);
    				break;
    			}
    			getflagbyname(state, vname, script, lineno, 1);
    		} else if (!strcasecmp(keyword, "DISPLAY")) {
    			lrci = 0;
    			wi = 0;
    
    			if (!(args = get_token(&buf, script, lineno))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_log(LOG_WARNING, "SUB definition missing name at line %d of %s\n", lineno, script);
    				break;
    			}
    			if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
    
    				ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY name at line %d of %s\n", args, lineno, script);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				break;
    			}
    			if (getdisplaybyname(state, vname, script, lineno, 0)) {
    				ast_log(LOG_WARNING, "State '%s' is already defined\n", vname);
    				break;
    			}
    
    			if (!(disp = getdisplaybyname(state, vname, script, lineno, 1)))
    
    Mark Spencer's avatar
    Mark Spencer committed
    				break;
    
    			if (!(args = get_token(&buf, script, lineno)) || strcasecmp(args, "IS")) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_log(LOG_WARNING, "Missing 'IS' at line %d of %s\n", lineno, script);
    				break;
    			}
    
    			if (!(args = get_token(&buf, script, lineno))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_log(LOG_WARNING, "Missing Column 1 text at line %d of %s\n", lineno, script);
    				break;
    			}
    			if (process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
    				ast_log(LOG_WARNING, "Token '%s' is not valid column 1 text at line %d of %s\n", args, lineno, script);
    				break;
    			}
    			if (strlen(tmp) > 20) {
    				ast_log(LOG_WARNING, "Truncating column one to 20 characters at line %d of %s\n", lineno, script);
    				tmp[20] = '\0';
    			}
    			memcpy(disp->data + 5, tmp, strlen(tmp));
    			disp->datalen = strlen(tmp) + 5;
    			disp->data[disp->datalen++] = 0xff;
    
    			args = get_token(&buf, script, lineno);
    			if (args && !process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
    				/* Got a column two */
    				if (strlen(tmp) > 20) {
    					ast_log(LOG_WARNING, "Truncating column two to 20 characters at line %d of %s\n", lineno, script);
    					tmp[20] = '\0';
    				}
    				memcpy(disp->data + disp->datalen, tmp, strlen(tmp));
    				disp->datalen += strlen(tmp);
    				args = get_token(&buf, script, lineno);
    			}
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    			while (args) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				if (!strcasecmp(args, "JUSTIFY")) {
    					args = get_token(&buf, script, lineno);
    					if (!args) {
    						ast_log(LOG_WARNING, "Qualifier 'JUSTIFY' requires an argument at line %d of %s\n", lineno, script);
    						break;
    					}
    					lrci = getjustifybyname(args);
    					if (lrci < 0) {
    						ast_log(LOG_WARNING, "'%s' is not a valid justification at line %d of %s\n", args, lineno, script);
    						break;
    					}
    				} else if (!strcasecmp(args, "WRAP")) {
    					wi = 0x80;
    				} else {
    					ast_log(LOG_WARNING, "'%s' is not a known qualifier at line %d of %s\n", args, lineno, script);
    					break;
    				}
    				args = get_token(&buf, script, lineno);
    			}
    			if (args) {
    				/* Something bad happened */
    				break;
    			}
    			disp->data[0] = 129;
    			disp->data[1] = disp->datalen - 2;
    			disp->data[2] = ((lrci & 0x3) << 6) | disp->id;
    			disp->data[3] = wi;
    			disp->data[4] = 0xff;
    		} else {
    			ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in PROGRAM\n", keyword);
    		}
    		break;
    	case STATE_INKEY:
    		if (process_returncode(state->key, keyword, buf, state, script, lineno)) {
    			if (!strcasecmp(keyword, "ENDKEY")) {
    				/* Return to normal operation and increment current key */
    				state->state = STATE_NORMAL;
    				state->key->defined = 1;
    				state->key->retstr[1] = state->key->retstrlen - 2;
    				state->key = NULL;
    			} else {
    				ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in SOFTKEY definition at line %d of %s\n", keyword, lineno, script);
    			}
    		}
    		break;
    	case STATE_INIF:
    		if (process_opcode(state->sub, keyword, buf, state, script, lineno)) {
    			if (!strcasecmp(keyword, "ENDIF")) {
    				/* Return to normal SUB operation and increment current key */
    				state->state = STATE_INSUB;
    				state->sub->defined = 1;
    				/* Store the proper number of instructions */
    				state->sub->ifdata[2] = state->sub->ifinscount;
    			} else if (!strcasecmp(keyword, "GOTO")) {
    
    				if (!(args = get_token(&buf, script, lineno))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    					ast_log(LOG_WARNING, "GOTO clause missing Subscript name at line %d of %s\n", lineno, script);
    					break;
    				}
    				if (process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
    					ast_log(LOG_WARNING, "'%s' is not a valid subscript name token at line %d of %s\n", args, lineno, script);
    					break;
    				}
    
    				if (!(newsub = getsubbyname(state, tmp, script, lineno)))
    
    Mark Spencer's avatar
    Mark Spencer committed
    					break;
    				/* Somehow you use GOTO to go to another place */
    				state->sub->data[state->sub->datalen++] = 0x8;
    				state->sub->data[state->sub->datalen++] = state->sub->ifdata[1];
    				state->sub->data[state->sub->datalen++] = newsub->id;
    				/* Terminate */
    				state->sub->data[state->sub->datalen++] = 0xff;
    				/* Increment counters */
    				state->sub->inscount++;
    				state->sub->ifinscount++;
    			} else {
    				ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in IF clause at line %d of %s\n", keyword, lineno, script);
    			}
    		} else
    			state->sub->ifinscount++;
    		break;
    	case STATE_INSUB:
    		if (process_opcode(state->sub, keyword, buf, state, script, lineno)) {
    			if (!strcasecmp(keyword, "ENDSUB")) {
    				/* Return to normal operation and increment current key */
    				state->state = STATE_NORMAL;
    				state->sub->defined = 1;
    				/* Store the proper length */
    				state->sub->data[1] = state->sub->datalen - 2;
    				if (state->sub->id) {
    					/* if this isn't main, store number of instructions, too */
    					state->sub->data[5] = state->sub->inscount;
    				}
    				state->sub = NULL;
    			} else if (!strcasecmp(keyword, "IFEVENT")) {
    
    				if (!(args = get_token(&buf, script, lineno))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    					ast_log(LOG_WARNING, "IFEVENT clause missing Event name at line %d of %s\n", lineno, script);
    					break;
    				}
    
    				if ((event = geteventbyname(args)) < 1) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    					ast_log(LOG_WARNING, "'%s' is not a valid event\n", args);
    					break;
    				}
    
    				if (!(args = get_token(&buf, script, lineno)) || strcasecmp(args, "THEN")) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    					ast_log(LOG_WARNING, "IFEVENT clause missing 'THEN' at line %d of %s\n", lineno, script);
    					break;
    				}
    				state->sub->ifinscount = 0;
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    				state->sub->ifdata = state->sub->data + state->sub->datalen;
    
    Mark Spencer's avatar
    Mark Spencer committed
    				/* Reserve header and insert op codes */
    				state->sub->ifdata[0] = 0x1;
    				state->sub->ifdata[1] = event;
    				/* 2 is for the number of instructions */
    				state->sub->ifdata[3] = 0xff;
    				state->sub->datalen += 4;
    				/* Update Subscript instruction count */
    				state->sub->inscount++;
    				state->state = STATE_INIF;
    			} else {
    				ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in SUB definition at line %d of %s\n", keyword, lineno, script);
    			}
    		}
    		break;
    	default:
    		ast_log(LOG_WARNING, "Can't process keyword '%s' in weird state %d\n", keyword, state->state);
    	}
    	return 0;
    }
    
    
    static struct adsi_script *compile_script(const char *script)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	FILE *f;
    
    	char fn[256], buf[256], *c;
    	int lineno = 0, x, err;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct adsi_script *scr;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (script[0] == '/')
    
    		ast_copy_string(fn, script, sizeof(fn));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	else
    
    		snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, script);
    
    
    	if (!(f = fopen(fn, "r"))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "Can't open file '%s'\n", fn);
    		return NULL;
    	}
    
    	if (!(scr = ast_calloc(1, sizeof(*scr)))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		fclose(f);
    		return NULL;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Create "main" as first subroutine */
    	getsubbyname(scr, "main", NULL, 0);
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    	while (!feof(f)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (!feof(f)) {
    			lineno++;
    			/* Trim off trailing return */
    			buf[strlen(buf) - 1] = '\0';
    			/* Strip comments */
    
    			if ((c = strchr(buf, ';')))
    
    Mark Spencer's avatar
    Mark Spencer committed
    				*c = '\0';
    
    Mark Spencer's avatar
    Mark Spencer committed
    				adsi_process(scr, buf, script, lineno);
    		}
    	}
    	fclose(f);
    	/* Make sure we're in the main routine again */
    	switch(scr->state) {
    	case STATE_NORMAL:
    		break;
    	case STATE_INSUB:
    		ast_log(LOG_WARNING, "Missing ENDSUB at end of file %s\n", script);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return NULL;
    	case STATE_INKEY:
    		ast_log(LOG_WARNING, "Missing ENDKEY at end of file %s\n", script);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return NULL;
    	}
    	err = 0;
    
    	/* Resolve all keys and record their lengths */
    
    	for (x = 0; x < scr->numkeys; x++) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (!scr->keys[x].defined) {
    			ast_log(LOG_WARNING, "Key '%s' referenced but never defined in file %s\n", scr->keys[x].vname, fn);
    			err++;
    		}
    	}
    
    	/* Resolve all subs */
    
    	for (x = 0; x < scr->numsubs; x++) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (!scr->subs[x].defined) {
    			ast_log(LOG_WARNING, "Subscript '%s' referenced but never defined in file %s\n", scr->subs[x].vname, fn);
    			err++;
    		}
    		if (x == (scr->numsubs - 1)) {
    			/* Clear out extension bit on last message */
    			scr->subs[x].data[2] = 0x80;
    		}
    	}
    
    	if (err) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return NULL;
    	}
    	return scr;
    }
    
    #ifdef DUMP_MESSAGES
    static void dump_message(char *type, char *vname, unsigned char *buf, int buflen)
    {
    	int x;
    	printf("%s %s: [ ", type, vname);
    
    	for (x = 0; x < buflen; x++)
    
    Mark Spencer's avatar
    Mark Spencer committed
    		printf("%02x ", buf[x]);
    	printf("]\n");
    }
    #endif
    
    
    static int adsi_prog(struct ast_channel *chan, const char *script)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct adsi_script *scr;
    
    	int x, bytes;
    
    	unsigned char buf[1024];
    
    
    	if (!(scr = compile_script(script)))
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    
    	/* Start an empty ADSI Session */
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    	if (ast_adsi_load_session(chan, NULL, 0, 1) < 1)
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    
    	/* Now begin the download attempt */
    
    	if (ast_adsi_begin_download(chan, scr->desc, scr->fdn, scr->sec, scr->ver)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* User rejected us for some reason */
    
    		ast_verb(3, "User rejected download attempt\n");
    
    		ast_log(LOG_NOTICE, "User rejected download on channel %s\n", ast_channel_name(chan));
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    
    	bytes = 0;
    	/* Start with key definitions */
    
    	for (x = 0; x < scr->numkeys; x++) {
    
    		if (bytes + scr->keys[x].retstrlen > 253) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* Send what we've collected so far */
    
    			if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
    				return -1;
    			}
    			bytes =0;
    		}
    		memcpy(buf + bytes, scr->keys[x].retstr, scr->keys[x].retstrlen);
    		bytes += scr->keys[x].retstrlen;
    #ifdef DUMP_MESSAGES
    		dump_message("Key", scr->keys[x].vname, scr->keys[x].retstr, scr->keys[x].retstrlen);
    #endif
    	}
    	if (bytes) {
    
    		if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
    			return -1;
    		}
    	}
    
    	bytes = 0;
    	/* Continue with the display messages */
    
    	for (x = 0; x < scr->numdisplays; x++) {
    
    		if (bytes + scr->displays[x].datalen > 253) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* Send what we've collected so far */
    
    			if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
    				return -1;
    			}
    			bytes =0;
    		}
    		memcpy(buf + bytes, scr->displays[x].data, scr->displays[x].datalen);
    		bytes += scr->displays[x].datalen;
    #ifdef DUMP_MESSAGES
    		dump_message("Display", scr->displays[x].vname, scr->displays[x].data, scr->displays[x].datalen);
    #endif
    	}
    	if (bytes) {
    
    		if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
    			return -1;
    		}
    	}
    
    	bytes = 0;
    	/* Send subroutines */
    
    	for (x = 0; x < scr->numsubs; x++) {
    
    		if (bytes + scr->subs[x].datalen > 253) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* Send what we've collected so far */
    
    			if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
    				return -1;
    			}
    			bytes =0;
    		}
    		memcpy(buf + bytes, scr->subs[x].data, scr->subs[x].datalen);
    		bytes += scr->subs[x].datalen;
    #ifdef DUMP_MESSAGES
    		dump_message("Sub", scr->subs[x].vname, scr->subs[x].data, scr->subs[x].datalen);
    #endif
    	}
    	if (bytes) {
    
    		if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
    			return -1;
    		}
    	}
    
    	bytes = 0;
    
    	bytes += ast_adsi_display(buf, ADSI_INFO_PAGE, 1, ADSI_JUST_LEFT, 0, "Download complete.", "");
    	bytes += ast_adsi_set_line(buf, ADSI_INFO_PAGE, 1);
    	if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY) < 0)
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    
    	if (ast_adsi_end_download(chan)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Download failed for some reason */
    
    		ast_verb(3, "Download attempt failed\n");
    
    		ast_log(LOG_NOTICE, "Download failed on %s\n", ast_channel_name(chan));
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    
    	ast_adsi_unload_session(chan);
    
    static int adsi_exec(struct ast_channel *chan, const char *data)
    
    	if (ast_strlen_zero(data))
    
    Mark Spencer's avatar
    Mark Spencer committed
    		data = "asterisk.adsi";
    
    	if (!ast_adsi_available(chan)) {
    
    		ast_verb(3, "ADSI Unavailable on CPE.  Not bothering to try.\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	} else {
    
    		ast_verb(3, "ADSI Available on CPE.  Attempting Upload.\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		res = adsi_prog(chan, data);
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    
    static int unload_module(void)
    
    	return ast_unregister_application(app);
    
    static int load_module(void)
    
    	if (ast_register_application_xml(app, adsi_exec))
    
    		return AST_MODULE_LOAD_FAILURE;
    	return AST_MODULE_LOAD_SUCCESS;
    
    AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Asterisk ADSI Programming Application",
    		.load = load_module,
    		.unload = unload_module,
    		.nonoptreq = "res_adsi",
    		);