home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / OPENSTEP / Networking / msend-3.3-I / MSMessage.m < prev    next >
Encoding:
Text File  |  1997-10-27  |  11.5 KB  |  572 lines

  1. /*
  2.   $Id: MSMessage.m,v 3.1 1997/10/27 11:22:42 lukeh Exp $
  3.  
  4.   Copyright (c) 1996, 1997 Luke Howard.
  5.   All rights reserved.
  6.  
  7.   Portions Copyright (C) 1993 Zik Saleeba <zik@zikzak.net>
  8.   Portions Copyright (C) 1993 Andrew Herbert <andrew@mira.net.au>
  9.   Portions Copyright (C) 1992 Sun Microsystems. Inc.
  10.  
  11.   Redistribution and use in source and binary forms, with or without
  12.   modification, are permitted provided that the following conditions
  13.   are met:
  14.   1. Redistributions of source code must retain the above copyright
  15.      notice, this list of conditions and the following disclaimer.
  16.   2. Redistributions in binary form must reproduce the above copyright
  17.      notice, this list of conditions and the following disclaimer in the
  18.      documentation and/or other materials provided with the distribution.
  19.   3. All advertising materials mentioning features or use of this software
  20.      must display the following acknowledgement:
  21.          This product includes software developed by Luke Howard.
  22.   4. The name of the other may not be used to endorse or promote products
  23.      derived from this software without specific prior written permission.
  24.  
  25.   THIS SOFTWARE IS PROVIDED BY LUKE HOWARD ``AS IS'' AND
  26.   ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  27.   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  28.   ARE DISCLAIMED.  IN NO EVENT SHALL LUKE HOWARD BE LIABLE
  29.   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  30.   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  31.   OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  32.   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  33.   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  34.   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  35.   SUCH DAMAGE.
  36.  */
  37.  
  38. #import <Foundation/Foundation.h>
  39.  
  40. #import "MSMessage.h"
  41.  
  42. #ifdef NeXT
  43. #import <libc.h>
  44. #import <sys/types.h>
  45. #import <sys/time.h>
  46. #import <sys/stat.h>
  47. #import <sys/socket.h>
  48. #import <netinet/in.h>
  49. #import <net/if.h>
  50. #import <arpa/inet.h>
  51. #import <netdb.h>
  52. #import <stdio.h>
  53. #import <fcntl.h>
  54. #import <string.h>
  55. #import <signal.h>
  56. #import <ctype.h>
  57. #import <unistd.h>
  58. #import <pwd.h>
  59. #import <sys/param.h>
  60. #import <errno.h>
  61. #import <stdarg.h>
  62. #endif
  63.  
  64. #import <msend.h>
  65.  
  66. #ifdef NeXT
  67. char *strdup(const char *str);
  68. #endif
  69.  
  70. static char *empty_arg = "";
  71. static id logDelegate = nil;
  72.  
  73. void mslog(char *message, ...);
  74.  
  75. void mslog(char *message, ...)
  76. {
  77.     NSString *string;
  78.     va_list ap;
  79.  
  80.     va_start(ap, message);
  81.     if (errno && errno != ENOENT) {
  82.         string = [[NSString alloc] initWithFormat:[NSString stringWithFormat:@"%s: %s", message, strerror(errno)] arguments:ap];
  83.     } else {
  84.         string = [[NSString alloc] initWithFormat:[NSString stringWithCString:message] arguments:ap];
  85.     }
  86.  
  87.     va_end(ap);
  88.  
  89.     if (logDelegate) {
  90.         [logDelegate log:string];
  91.     } else {
  92.         NSLog(string);
  93.     }
  94.     va_end(ap);
  95.     [string release];
  96. }
  97.  
  98. @interface MSMessage (InternalMethods)
  99. - (BOOL)assembleMessage;
  100. - (BOOL)udpMsg;
  101. - (BOOL)tcpMsg;
  102. @end
  103.  
  104. @implementation MSMessage
  105. /* vend a new message object */
  106. + (MSMessage *)message
  107. {
  108.     return [[self class] messageTo:nil];
  109. }
  110.  
  111. + (MSMessage *)messageTo:(NSString *)recipient
  112. {
  113.     return [[[self class] alloc] initWithRecipient:recipient];
  114. }
  115.  
  116. - init
  117. {
  118.     return [self initWithRecipient:nil];
  119. }
  120.  
  121. - initWithRecipient:(NSString *)recipient
  122. {
  123.     [super init];
  124.  
  125.     if (recipient != nil)
  126.         [self setRecipient:recipient];
  127.  
  128.     use_tcp = NO;
  129.     broadcasting = NO;
  130.  
  131.     messageBody = [[NSMutableString alloc] init];
  132.  
  133.     retries = 4;
  134.     Sin.sin_family = AF_INET;
  135.  
  136.     errno = 0;
  137.  
  138.     return self;
  139. }
  140.  
  141. + (void)setLogDelegate:(id <MSLogging>)delegate
  142. {
  143.     [delegate retain];
  144.     [logDelegate release];
  145.     logDelegate = delegate;
  146. }
  147.  
  148. - (void)log:(NSString *)err
  149. {
  150.     NSLog(err);
  151. }
  152.  
  153. - (void)setPortToDefault
  154. {
  155.     struct servent *sp;
  156.  
  157.     sp = getservbyname("message", use_tcp ? "tcp" : "udp" );
  158.     if(sp)
  159.         Sin.sin_port = sp->s_port;
  160.     else
  161.         Sin.sin_port = htons(18); /* from the RFC */
  162. }
  163.  
  164. - (void)setUseTcp:(BOOL)useTcp
  165. {
  166.     use_tcp=useTcp;
  167. }
  168.  
  169. - (BOOL)useTcp
  170. {
  171.     return use_tcp;
  172. }
  173.  
  174. - (void)dealloc
  175. {
  176.     if (_recipient_ptr)
  177.         free(_recipient_ptr);
  178.     [messageBody release];
  179.     [super dealloc];
  180. }
  181.  
  182. /* accessor methods for the recipient (eg. user@host) */
  183. - (void)setRecipient:(NSString *)aRecipient
  184. {
  185.     char local_name[MAXHOSTNAMELEN];
  186.  
  187.     _recipient_ptr = strdup([aRecipient cString]);
  188.  
  189. /* (almost) verbatim from msend.c */
  190.     host = (char *)strchr(_recipient_ptr, '@');
  191.  
  192.     if (host == NULL)
  193.         host = empty_arg;
  194.     else
  195.         *host++ = '\0';
  196.  
  197.     term = (char *)strchr(_recipient_ptr, ':');
  198.     if(term == NULL)
  199.         term = empty_arg;
  200.     else
  201.         *term++ = '\0';
  202.     if(!strcmp(term, "all"))        /* external form is "all" */
  203.         strcpy(term, "*");              /* protocol uses "*" */
  204.  
  205.     user = _recipient_ptr;
  206.  
  207.  
  208.     if (host[0] == '\0')
  209.         {
  210.         gethostname(local_name, sizeof(local_name));
  211.         host = strdup(local_name); /* local_name is local to our stack */
  212.         }
  213.  
  214. }
  215.  
  216. - (NSString *)host
  217. {
  218.     return [NSString stringWithCString:host];
  219. }
  220.  
  221. - (NSString *)user
  222. {
  223.     return [NSString stringWithCString:user];
  224. }
  225.  
  226. - (NSString *)term
  227. {
  228.     return [NSString stringWithCString:term];
  229. }
  230.  
  231. - (void)setPort:(short)port
  232. {
  233.     Sin.sin_port = htons(port);
  234. }
  235.  
  236. - (void)setRetries:(int)r
  237. {
  238.     retries = r;
  239. }
  240.  
  241. - (int)retries
  242. {
  243.     return retries;
  244. }
  245.  
  246. - (short)port
  247. {
  248.     return ntohs(Sin.sin_port);
  249. }
  250.  
  251. - (void)setBroadcasting:(BOOL)doBroadcasting
  252. {
  253.     broadcasting = doBroadcasting;
  254. }
  255.  
  256. - (BOOL)broadcasting
  257. {
  258.     return broadcasting;
  259. }
  260.  
  261. - (BOOL)assembleMessage
  262. {
  263.     const char        *msg_text = [messageBody cString];
  264.  
  265.     char        linebuff[256];
  266.     char        *dp, *lp;
  267.     FILE        *sigfile;
  268.     char        *signature;
  269.     char        *homedir;
  270.     struct passwd    *pwd;
  271.     int        buflen;
  272.  
  273.     buflen = 1024;
  274.     msg = malloc(buflen);
  275.     if (msg == NULL)
  276.         return NO; /* out of memory */
  277.  
  278.     *msg = 'B'; /* as per RFC */
  279.     msg_len = 1;
  280.  
  281.     filter(user);
  282.     append_buffer(&msg, &buflen, &msg_len, user, 1);
  283.     filter(term);
  284.     append_buffer(&msg, &buflen, &msg_len, term, 1);
  285.  
  286.     if (msg_text)
  287.         do_line(&msg, &buflen, &msg_len, msg_text);
  288.     else
  289.         return NO;
  290.  
  291.     append_buffer(&msg, &buflen, &msg_len, "",  1);
  292.     lp = (char *)getlogin();
  293.     if (lp == NULL || *lp == '\0')
  294.         {
  295.         struct passwd *me;
  296.         me = getpwuid(getuid());
  297.         if (me == NULL)
  298.             return NO; /* memory leak XXX */
  299.         lp = me->pw_name;
  300.         }
  301.  
  302.     append_buffer(&msg, &buflen, &msg_len, lp, 1);
  303.  
  304.     dp = (char *)ttyname(2);
  305.     if (dp == NULL)
  306.         append_buffer(&msg, &buflen, &msg_len, "", 1);
  307.     else
  308.         append_buffer(&msg, &buflen, &msg_len, dp, 1);
  309.  
  310.     sprintf(linebuff, "%ld", time(NULL));
  311.     append_buffer(&msg, &buflen, &msg_len, linebuff, 1);
  312.  
  313.     homedir = (char *)getenv("HOME");
  314.     if (homedir == NULL)
  315.         {
  316.         pwd = (struct passwd *)getpwnam(lp); /* fixed bug in msend.c */
  317.         homedir = pwd->pw_dir;
  318.         }
  319.     sprintf(linebuff, "%s/.msgsig", homedir);
  320.     signature = "";
  321.     sigfile = fopen(linebuff, "r");
  322.     if (sigfile != NULL)
  323.         {
  324.                 linebuff[sizeof(linebuff)-1] = '\0';
  325.                 if (fgets(linebuff, sizeof(linebuff)-1, sigfile) != NULL) {
  326.                         if (linebuff[strlen(linebuff)-1] == '\n')
  327.                                 linebuff[strlen(linebuff)-1] = '\0';
  328.                         filter(linebuff);
  329.                         signature = linebuff;
  330.                 }
  331.                 fclose(sigfile);
  332.         }
  333.  
  334.     append_buffer(&msg, &buflen, &msg_len, signature, 1);
  335.  
  336.     return YES;
  337. }
  338.  
  339. - (void)setDebugging:(BOOL)flag
  340. {
  341.     debug=flag;
  342. }
  343.  
  344. - (BOOL)udpMsg
  345. {
  346.     int r = retries;
  347.     struct sockaddr_in *sp = &Sin;
  348.     int s, delivered = 0;
  349.     struct sockaddr_in SIN;
  350.     fd_set ready;
  351.     struct timeval to;
  352.     int rval, toutwait, i;
  353.  
  354.     if (debug) NSLog(@"invoked udpMsg(...,%d)",r);
  355.  
  356.     r = retries; /* local copy */
  357.  
  358.         if((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
  359.         mslog("unable to open socket");
  360.         return NO;
  361.         }
  362.  
  363.     if (broadcasting) {
  364.         i = 1;
  365.         if(setsockopt(s, SOL_SOCKET, SO_BROADCAST, &i, sizeof i) < 0) {
  366.             mslog("unable to configure socket for broadcast");
  367.                     return NO;
  368.         }
  369.         find_broadcast_addresses(s);
  370.     }
  371.  
  372.     SIN.sin_family = AF_INET;
  373.     SIN.sin_addr.s_addr = htonl(INADDR_ANY);
  374.     SIN.sin_port = htons(0);
  375.  
  376.     if(bind(s, (struct sockaddr *)&SIN, sizeof SIN) < 0) {
  377.         mslog("unable to set local socket address");
  378.         return NO;
  379.     }
  380.  
  381.     toutwait = 3; /* starts at 3 secs and backs off by 2 secs every time */
  382.  
  383.     while(r--) {
  384.         if (debug) NSLog(@"sending message...");
  385.         if (broadcasting) {
  386.                         for (i = 0; i < if_n; i++){
  387.                                 if_a[i].sin_port = sp->sin_port;
  388.                                 if(debug) NSLog(@"sending message to %s",
  389.                                         inet_ntoa(if_a[i].sin_addr));
  390.                                 if(sendto(s, msg, msg_len, 0, (struct sockaddr *)&(if_a[i]),
  391.                                 sizeof if_a[i]) < 0) {
  392.                     mslog("unable to send message");
  393.                                         return NO;
  394.                                 }
  395.                         }
  396.         }
  397.         else {
  398.             if (sendto(s, msg, msg_len, 0, (struct sockaddr *)sp, sizeof(*sp)) < 0) {
  399.                 mslog("unable to send message");
  400.                 return NO;
  401.             }
  402.         }
  403.         if (r) {
  404.             to.tv_sec = toutwait;
  405.             to.tv_usec = 0;
  406.             FD_ZERO(&ready);
  407.             FD_SET(s, &ready);
  408.             rval = select(20, &ready, (fd_set *)0, (fd_set *)0, &to);
  409.             if (rval < 0)
  410.                 NSLog(@"Interrupt");
  411.             if (rval == 1) {
  412.                 delivered = 1;
  413.                 udp_decode_ack(s);
  414.                 break;
  415.             }
  416.             toutwait += 2;
  417.         }
  418.     }
  419.     if (!delivered)
  420.         mslog("Message unacknowledged - may not have been received");
  421.  
  422.     close(s);
  423.     return YES;
  424.  
  425. }
  426.  
  427. - (BOOL)tcpMsg
  428. {
  429.     int s;
  430.     struct sockaddr_in SIN; /* so as not to hide instance var */
  431.     struct sockaddr_in *sp = &Sin;
  432.     char rcvbuf[256];
  433.     int rval;
  434.  
  435.     if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { return NO; }
  436.  
  437.     SIN.sin_family = AF_INET;
  438.     SIN.sin_addr.s_addr = htonl(INADDR_ANY);
  439.     SIN.sin_port = htons(0);
  440.  
  441.     if(bind(s, (struct sockaddr *)&SIN, sizeof SIN) < 0) {
  442.         mslog("unable to bind local socket address");
  443.         return NO;
  444.     }
  445.  
  446.     if(connect(s, (struct sockaddr *)sp, sizeof(*sp)) < 0) {
  447.         mslog("unable to connect to TCP server");
  448.         return NO;
  449.     }
  450.  
  451.     if(write(s, msg, msg_len) < 0) {
  452.             (void)close(s);
  453.         mslog("unable to send message");    
  454.         return NO;
  455.     }
  456.  
  457.     rval = read(s, rcvbuf, sizeof rcvbuf);
  458.     if (rval < 1) {
  459.             close(s);
  460.         mslog("No reply received");
  461.         return NO;
  462.     }
  463.  
  464.     rcvbuf[(rval < sizeof(rcvbuf)) ? rval : sizeof(rcvbuf)-1] = 0;
  465.     if (rcvbuf[0] == '+') {
  466.         if (debug) NSLog(@"Message delivered to recipient (%s)\n", rcvbuf+1);
  467.         return YES;
  468.     }
  469.  
  470.     else if (rcvbuf[0] == '-') {
  471.         mslog("Message wasn't delivered - %s.", rcvbuf+1);
  472.         return NO;
  473.     }
  474.  
  475.     else {
  476.         mslog("Message wasn't delivered");
  477.         return NO;
  478.     }
  479.  
  480.     return NO;
  481. }
  482.  
  483. /* accessor methods for the message itself */
  484. - (void)setMessage:(NSString *)message
  485. {
  486.     [messageBody setString:message];
  487. }
  488.  
  489. - (void)appendMessage:(NSString *)message
  490. {
  491.     [messageBody appendString:message];
  492. }
  493.  
  494. - (NSString *)message
  495. {
  496.     return messageBody;
  497. }
  498.  
  499.  
  500. - (BOOL)ready
  501. {
  502.     if (host == NULL)
  503.         return NO;
  504.     if (user == NULL)
  505.         return NO;
  506.     if ([messageBody length] == 0)
  507.         return NO;
  508.  
  509.     return YES;
  510. }
  511.  
  512. /* post the message */
  513. - (BOOL)post
  514. {
  515.     struct hostent *hp;
  516.  
  517.     if ([self ready] == NO)
  518.         return NO;
  519.  
  520.     if (Sin.sin_port == 0)
  521.         [self setPortToDefault];
  522.  
  523.     if (!broadcasting)
  524.         {
  525.         hp = gethostbyname(host);
  526.  
  527.         if (hp == NULL)
  528.             {
  529.             mslog("unknown host: %s", host);
  530.             return NO;
  531.             }
  532.         memcpy((char *)&Sin.sin_addr, (char *)hp->h_addr, hp->h_length);
  533.         }
  534.  
  535. /* assemble the message */
  536.     if ([self assembleMessage] == NO)
  537.         return NO;
  538.  
  539.     
  540.     if (broadcasting)
  541.         return [self udpMsg];
  542.  
  543.     if (use_tcp)
  544.         if ([self tcpMsg] == NO)
  545.             return NO;
  546.  
  547.     [self udpMsg];
  548.     
  549.     return YES;
  550.             
  551. }
  552. @end
  553.  
  554. #ifdef NeXT
  555. #if NS_TARGET_MINOR == 1
  556. char *strdup(const char *str)
  557. {
  558.     size_t len = strlen(str) + 1;
  559.     char *copy;
  560.  
  561.     copy = (char *)malloc(len);
  562.  
  563.     if (copy == NULL) return NULL;
  564.  
  565.     bcopy(str, copy, len);
  566.  
  567.     return copy;
  568. }
  569. #endif
  570. #endif
  571.  
  572.