/* clicksms: A tcpip client program which enables sms messages to be sent to a list of mobile phones from an OpenVMS system via a HTTP POST request to Clickatell. Clickatell is a fast, reliable SMS gateway based in South Africa but has world-wide coverage. For Australia, it supports Hutchison, Orange, Telstra, Virgin, Optus, Vodafone. It is subscription-based but reasonably cheap (7c per msg) so this is a simple convenient way for a VMS system manager to issue alerts with DCL. But remember, this works only if your Internet gateway is up. Tested under OpenVMS VAX/AXP 7.3. Don't have access Tru64, please try to build it and let me know. NOTE: mobile number is international format ie. country code minus prefix plus number (eg. 61422765825 in Australia). Make: cc clicksms link clicksms Run: Set up clicksms.conf first. It consists of 2 lines: api.clickatell.com /http/sendmsg 80 Line 1: Do not edit unless Clickatell says so. Line 2: authorisation info from Clickatell. $ clicksms:==$util:[exe]clicksms.exe $ clicksms 61420123456,61411182193 "Warning: DUA0 90% full" Connected to server at 196.7.150.220:80 Status: OK License: Freeware Author: Alex Wong, Innovia Pty Ltd, 24-Sep-2003, alw1746@yahoo.com.au */ #include #include #include #include #include #include #include #include /* forward declarations */ int main(int argc,char *argv[] ); void get_serv_addr(void *,char *node); int URLencode (const char *plain,char *encode,int maxlen); /*---- start program ----*/ int main(int argc,char *argv[] ) { int sockfd,blen,port,rlen; char buf[1024],hdr[1024]; struct sockaddr_in serv_addr; FILE *fp; char inifile[]="clicksms.conf"; /* config file */ char line[1024],user[32],pwd[32],api[32],text[200],host[50],page[50]; char *p,*p1,*eob; if (argc < 3) { printf("Usage: clicksms \"message\""); exit(EXIT_FAILURE); } /* open config information file */ if ((fp=fopen(inifile,"r"))==NULL) { perror( "Failed to open config info file" ); exit( EXIT_FAILURE ); } /* get clickatell url, port */ fscanf(fp,"%s %s %d",host,page,&port); /* get subscriber access info */ fscanf(fp,"%s %s %s",api,user,pwd); /* sms text must be url-encoded */ URLencode (argv[2], text, sizeof(text)); /* build POST content */ sprintf(line,"api_id=%s&user=%s&password=%s&to=%s&text=%s",api,user,pwd, argv[1],text); sprintf(buf,"%d",strlen(line)); /* content length */ /* build HTTP POST header */ strcpy(hdr,"POST "); strcat(hdr,page); /* clickatell sms page */ strcat(hdr," HTTP/1.1\n"); strcat(hdr,"Host: "); strcat(hdr,host); /* clickatell server url */ strcat(hdr,"\nUser-Agent: Mozilla/4.0\n"); strcat(hdr,"Content-Type: application/x-www-form-urlencoded\n"); strcat(hdr,"Content-Length: "); strcat(hdr,buf); /* content length */ strcat(hdr,"\n\n"); /* blank line required */ strcat(hdr,line); /* content itself */ /* printf("DEBUG: %s\n",hdr); */ /* set up inet server address block */ memset( &serv_addr, 0, sizeof(serv_addr) ); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(port); get_serv_addr(&serv_addr.sin_addr,host); /* get socket */ if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) { perror( "Failed to create socket" ); exit( EXIT_FAILURE ); } /* connect to clickatell server */ if (connect(sockfd,(struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0 ) { perror( "Failed to connect to server" ); exit( EXIT_FAILURE ); } else { printf("Connected to server at %s:%d\n", inet_ntoa(serv_addr.sin_addr), ntohs(serv_addr.sin_port) ); } /* HTTP POST sms request */ if ( send(sockfd, hdr, sizeof(hdr),0) < 0 ) { perror( "Error sending data to server" ); exit( EXIT_FAILURE ); } /* read server reply into a buffer until conn closed*/ p = &buf[0]; blen=sizeof(buf); do { rlen=recv(sockfd,p,blen,0); /* fill up buffer in a loop */ if (rlen > 0) { /* got data? */ p += rlen; /* next free spot */ blen -= rlen; /* available free space */ if (blen == 0) { /* buffer full? */ --p; /* allow for \0 at eob */ break; } } else break; /* server closed conn */ } while (blen > 0); *(++p)='\0'; /* string it */ /* printf("DEBUG: %s\n",buf); */ /* scan reply for error message */ p=strstr(buf,"ERR: "); if (p == NULL) { printf("Status: OK"); } else { eob = &buf[0] + strlen(buf); /* get end of buffer */ for (p1=p; p1 < eob; p1++) { /* look for end of line */ if (*p1 == '\n') { /* LF is end of line */ *p1='\0'; /* turn it into string */ break; } } printf("Status: %s\n",p); } /* clean up and exit */ if ( shutdown(sockfd, 2) < 0 ) { perror( "Failed to shutdown server connection" ); exit( EXIT_FAILURE ); } if ( close(sockfd) < 0 ) { perror( "Failed to close socket" ); exit( EXIT_FAILURE ); } exit( EXIT_SUCCESS ); } /* end main */ /*---- get ip address of server into address block ----*/ void get_serv_addr(void *addrptr, char *node) { struct in_addr val; struct hostent *host; val.s_addr = inet_addr(node); if ( val.s_addr != INADDR_NONE ) { memcpy( addrptr, &val, sizeof(struct in_addr) ); } if ( (host = gethostbyname(node)) ) { /* dns lookup */ memcpy( addrptr, host->h_addr, sizeof(struct in_addr) ); } } /* end get_serv_addr */ /*---- helper routine for URLencode ----*/ char bin2hex (char ch){ /* return ascii char for hex value of rightmost 4 bits of input */ ch = ch & 0x0f; /* mask off right nibble - & bitwise AND*/ ch += '0'; /* make ascii '0' - '9' */ if (ch > '9') ch += 7; /* account for 7 chars between '9' and 'A' */ return (ch); } /*---- helper routine for URLencode ---- */ int AlphaNumeric (char ch){ return ((ch >='a') && (ch <= 'z') || /* logical AND &&, OR || */ (ch >='A') && (ch <= 'Z') || (ch >='0') && (ch <= '9') ); } /*---- encode plaintext string into url-encoded format ----*/ int URLencode (const char * plain, char * encode, int maxlen) { char ch; /* each char, use $t2 */ char *limit; /* point to last available location in encode */ char *start; /* save start of encode for length calculation */ start=encode; limit = encode + maxlen - 4; /* need to store 3 chars and a zero */ ch = *plain++; /* get first character */ while (ch != 0) { /* end of string, asciiz */ if (ch == ' ') * encode ++ = '+'; else if (AlphaNumeric (ch)) * encode ++ = ch; else { * encode ++ = '%'; * encode ++ = bin2hex (ch >> 4); /*shift right for left nibble*/ * encode ++ = bin2hex (ch); /* right nibble */ } ch = *plain ++; /* ready for next character */ if (encode > limit){ *encode = 0; /* still room to terminate string */ return (-1); /* get out with error indication */ } } * encode = 0; /* store zero byte to terminate string */ return (encode - start); /* done, return count of characters */ } /* end URLencode */