]> code.citadel.org Git - citadel.git/blob - libCxClient/src/misc.c
0efd590843fdaff2149a83e82801e848171d5a3e
[citadel.git] / libCxClient / src / misc.c
1 /**
2  ** libCxClient - Citadel/UX Extensible Client API
3  ** Copyright (c) 2000, Flaming Sword Productions
4  ** Copyright (c) 2001, The Citadel/UX Consortium
5  ** All Rights Reserved
6  **
7  ** Module: misc.o
8  ** Date: 2000-11-16
9  ** Last Revision: 2000-11-16
10  ** Description: Miscellaneous support functions.
11  ** CVS: $Id$
12  **/
13 #include        <stdio.h>
14 #include        <stdlib.h>
15 #include        <signal.h>
16 #include        <string.h>
17 #include        <CxClient.h>
18 #include        "autoconf.h"
19 #include        "uname.h"
20
21 /**
22  ** Function which we have hooked to.
23  **/
24 static void     (*_MiExpFunc)(const char*, const char*) = 0;
25
26 /**
27  ** CxRevision(): Report revision information about libCxClient.
28  **/
29 float           CxRevision() {
30         return(atof(VERSION));
31 }
32
33 /**
34  ** CxSerialize(): Take a pipe-separated string & convert it into a 2-dimensional
35  ** array.  THIS FUNCTION WILL HANDLE AN ABSOLUTE MAXIMUM OF 50 ARGUMENTS.
36  **/
37 void            CxSerialize(const char *s, char **Ser) {
38 char            **ap, *argv[50], *end;
39 int             i;
40  
41         DPF((DFA,"Serializing '%s'",s));
42
43         for (ap = argv; (*ap = (char *)strsep((char **)&s, "|")) != NULL;) {
44                 if (++ap >= &argv[50]) {
45                         break;
46                 }
47         }
48         end = (char *)(ap - 1);
49  
50         i = 0;
51         for(ap=&argv[0]; (char *)ap <= end; ap++) {
52                 Ser[i] = *ap;
53                 i++;
54         }
55
56         Ser[i] = 0;
57
58         DPF((DFA,"Done"));
59
60
61 /**
62  ** CxMiExpSend(): Send an express message.  Think of this like AIM
63  ** without AOL...
64  **
65  ** [Expects]
66  **  (char *) user: Username
67  **  (char *) msg: Express message.
68  **
69  ** [Returns]
70  **  Success: 0
71  **  Failure; Unknown Error: 1
72  **/
73 int             CxMiExpSend(int id, const char *user, const char *msg) {
74 char            *xmit, buf[255];
75 int             rc;
76
77
78         /**
79          ** Need to check the server revision on this connection
80          ** handle.  If the server only supports short-style
81          ** express messages, we're gonna have to implement it
82          ** that way (and strip out everything after the newline
83          ** in msg?  or s/\n/ /g...?)
84
85          ** For reasons specified in session.txt, the following line
86          ** will never be guaranteed to work.. .
87         sprintf(xmit,"SEXP %s|%s",user, msg);
88
89          **/
90         xmit = (char *)CxMalloc(strlen(user)+strlen(msg) + 7);
91         sprintf(xmit,"SEXP %s|-",user);
92         CxClSend(id, xmit);
93         CxFree(xmit);
94
95         rc = CxClRecv( id, buf );
96         if( CHECKRC( rc, RC_SENDLIST ) ) {
97                 DPF((DFA, "Sending:\n%s\n", msg));
98                 CxClSend( id, msg );
99                 DPF((DFA, "END STREAM.", msg));
100                 CxClSend( id, "000" );
101
102                 DPF((DFA,"Express Message accepted by server.")); /** we hope... **/
103                 return(0);
104
105         } else {
106                 DPF((DFA,"Express Message _rejected_ by server."));
107                 DPF((DFA,"(Operation not supported.)"));
108                 return(1);
109         }
110 }
111
112 /**
113  ** CxMiExpRecv(): Receive an express message.  Usually, this is
114  ** called after a NOOP loop returns RC_xxxx...
115  **
116  ** [Returns]
117  **  Success: Ptr to malloc()ed EXPRMESG struct.  [*]
118  **  Failure: NULL
119  **/
120 EXPRMESG        *CxMiExpRecv( int id ) {
121 char            buf[255], *Ser[20];
122 EXPRMESG        *toret;
123 int             rc;
124
125         /**
126          ** Ask the server for the latest Express Message [GEXP].
127          **/
128         DPF((DFA,"Receive Express Message"));
129         CxClSend(id, "GEXP");
130         rc = CxClRecv(id, buf);
131         DPF((DFA,"buf=%s\n",buf));
132         toret = 0L;
133
134         /**
135          ** If rc==RC_LISTING, then we have a valid Express Message.
136          **/
137         DPF((DFA,"Checking result = ", rc));
138         if( CHECKRC(rc, RC_LISTING)) {
139
140                 DPF((DFA,"This is an express message.  Working on receiving..."));
141                 toret = (EXPRMESG *) CxMalloc( sizeof(EXPRMESG) );
142                 bzero( &toret, sizeof(EXPRMESG) );
143
144                 CxSerialize( buf, (char **)&Ser );
145
146                 toret->more_follows = atoi( Ser[0] );
147                 toret->timestamp = (time_t) strtoul( Ser[1], 0, 10 );
148                 toret->flags = atoi( Ser[2] );
149                 strcpy( toret->sender, Ser[3] );
150                 strcpy( toret->node, Ser[4] );
151                 toret->message = 0L;
152                 do {
153                         if((rc = CxClRecv(id, buf))) {
154                                 DPF((DFA, "toret->message @0x%08x", toret->message));
155                                 DPF((DFA, "+++ %s", buf));
156                                 if(toret->message) {
157                                         toret->message = (char *) malloc(strlen(buf)+1);
158                                 } else {
159                                         toret->message = (char *) realloc(toret->message, strlen(toret->message)+strlen(buf)+1);
160                                 }
161                                 strcat(toret->message, buf);
162                         }
163                 } while( rc < 0 );
164
165 /****           toret = (char *) CxMalloc(strlen(buf)+2);
166                 strcpy(toret,buf);
167                 strcat(toret,"|");
168                 do {
169                         if((rc = CxClRecv(id, buf))) {
170                                 DPF((DFA,"%s",buf));
171                                 toret = (char *) realloc(toret, strlen(toret)+strlen(buf)+1);
172                                 strcat(toret,buf);
173                         }
174                 } while(rc<0);
175  ****/
176         }
177
178         return(toret);
179 }
180
181 /**
182  ** CxMiExpCheck(): Check to see if there are any EXPress MEssages
183  ** waiting for the currently logged-in user.
184  **
185  ** [Returns]
186  **  Message Waiting: 1
187  **  No Messages: 0
188  **/
189 int             CxMiExpCheck( int id ) {
190 int             rc;
191 char            buf[255];
192
193         DPF((DFA,"Sending NOOP"));
194         CxClSend(id, "NOOP");
195         DPF((DFA,"Checking response"));
196         rc = CxClRecv(id, buf);
197
198         /**
199          ** CxClRecv() tacks on a RC_MESGWAIT flag to the result
200          ** code upon seeing a Message Waiting note from the
201          ** server.  This behaviour is deprecated in updated
202          ** versions of Citadel/UX, but is still included for
203          ** compatibility's sake.
204          **/
205         if(CHECKRC(rc,RC_MESGWAIT)) {
206                 DPF((DFA,"Express Message waiting!"));
207                 return(1);
208
209         } else {
210                 DPF((DFA,"No express message, loser."));
211                 return(0);
212         }
213 }
214
215 /**
216  ** _CxMiExpHook(): Hook to RC_901 messages [Contain express messages]
217  ** [Not Intended For External Use]
218  **/
219 static
220 void            _CxMiExpHook(int id, const void* data) {
221 char            buf[512], *user_buf, *data_buf;
222 int             rc;
223
224         DPF((DFA, "*ASYN* Received RC_901 message on CXID %d", id));
225         DPF((DFA, "Raw data: \"%s\"", (char *)data));
226
227         /**
228          ** Fetch the sending user's information from the data stream.
229          **/
230         rc = CxClRecv(id, buf);
231         user_buf = (char *)CxMalloc(strlen(buf)+1);
232         strcpy(user_buf, buf);
233
234         DPF(( DFA, "user_buf @0x%08x", data_buf ));
235         DPF(( DFA, "user_buf: %s", user_buf ));
236
237         DPF(( DFA, "allocating data_buf" ));
238         data_buf = (char *)CxMalloc(1);
239         *(data_buf) = 0;
240         DPF(( DFA, "allocated data_buf @0x%08x", data_buf ));
241
242         /**
243          ** If this is a multi-line message, then fetch the message
244          ** from the datas stream.
245          **/
246         if(CHECKRC(rc, RC_LISTING)) {
247                 do {
248                         DPF(( DFA, "data_buf @0x%08x", data_buf ));
249                         DPF(( DFA, "data_buf: %s", data_buf ));
250                         rc = CxClRecv( id, buf );
251                         if( rc<0 ) {
252                                 DPF(( DFA, "data_buf szlen %d", strlen(data_buf) + strlen(buf) +1));
253                                 if( strlen(buf) ) {
254                                         realloc(data_buf, strlen(data_buf)+strlen(buf)+1);
255                                         strcat(data_buf, buf);
256                                 }
257                         }
258                 } while(rc < 0);
259         }
260
261         /**
262          ** Pass this information off to the user's function.
263          **/
264         DPF(( DFA, "Completed.  Received the following data:\n: user_buf: %s\n: data_buf: %s\n",
265                 user_buf, data_buf ));
266
267         DPF(( DFA, "->_MiExpFunc() -- USERLAND"));
268         _MiExpFunc(user_buf, data_buf);
269         DPF(( DFA, "<-_MiExpFunc() -- /USERLAND"));
270
271         DPF(( DFA, "Freeing pointer user_buf"));
272         CxFree(user_buf);
273         DPF(( DFA, "Freeing pointer data_buf"));
274         CxFree(data_buf);
275
276         DPF(( DFA, "Done!"));
277 }
278
279 /**
280  ** CxMiExpHook(): We will allow the user to hook themselves into
281  ** our express message handler.  Only one function is permitted to
282  ** hook here.
283  **
284  ** [Expects]
285  **  func: The function that the user has written to handle Express
286  **        Messages.  
287  **        void func( const char *USER_FROM, const char *TEXT);
288  **/
289 void            CxMiExpHook(void (*func)(const char*, const char *)) {
290
291         DPF((DFA, "Hooking user func@0x%08x",func));
292
293         /**
294          ** If libCxClient has not already hooked this type of
295          ** message, we need to go ahead and hook it to our
296          ** internal routing function.
297          **/
298         if(!CxClCbExists(901)) {
299                 DPF((DFA, "Hooking into RC_901"));
300                 CxClCbRegister(901, _CxMiExpHook);
301         }
302
303         /**
304          ** Now, register the user's hooked function with
305          ** ourselves.  This instructs _CxMiExpHook() on
306          ** where to route data.
307          **/
308         DPF((DFA,"Registering user hook"));
309         _MiExpFunc = func;
310
311         DPF((DFA,"Ok, at this point, RC_901 messages should be routed to the user."));   
312         DPF((DFA,"Don't blame me if it doesn't work.  You told me what to do, Brian."));
313 }
314
315 /**
316  ** CxMiMessage(): Read a system message file, ONE LINE AT A TIME.
317  ** This function will return the current line sent by the server,
318  ** and will return NULL on completion.  The caller is responsible
319  ** for freeing EACH LINE OF MEMORY passed to it.
320  **
321  ** [Expects]
322  **  (char *)file: The name of the file we want, or NULL to continue
323  **  the current stream...
324  **
325  ** [Returns]
326  **  Success: Ptr to malloc()ed file data.  [*]
327  **  Failure; File not found: NULL
328  **/
329 char            *CxMiMessage(int id, const char *file) {
330 char            buf[255], *toret;
331 int             rc;
332
333         if((file!=NULL) && file[0]) {
334                 DPF((DFA,"Requesting %s from server.",file));
335                 sprintf(buf,"MESG %s", file);
336                 CxClSend(id, buf);
337                 rc = CxClRecv(id, buf);
338                 if(CHECKRC(rc, RC_LISTING)) {
339                         DPF((DFA,"Retrieving line from file..."));
340                         rc = CxClRecv(id, buf);
341                         if(rc < 0) {
342                                 toret = (char *)CxMalloc(strlen(buf)+1);
343                                 strcpy(toret, buf);
344                                 DPF((DFA,"MEM/MDA:\t-1\t@0x%08x (Needs manual deallocation)", toret)); 
345                                 return(toret);
346                         } else {
347                                 return(NULL);
348                         }
349                 } else {
350                         return(NULL);
351                 }
352         } else {
353
354                 DPF((DFA,"Retrieving line from file..."));
355                 rc = CxClRecv( id, buf);
356                 if(rc < 0) {
357                         toret = (char *)CxMalloc(strlen(buf)+1);
358                         strcpy(toret,buf);
359                         DPF((DFA,"MEM/MDA:\t-1\t@0x%08x (Needs manual deallocation)", toret)); 
360                         return(toret);
361                 } else {
362                         return(NULL);
363                 }
364         }
365
366         /**
367          ** Insurance measure...
368          **/
369         return(NULL);
370 }
371
372 /**
373  ** CxMiImage(): Read a system image.  Images are defined by the 
374  ** Citadel/UX session protocol manual as always being in GIF
375  ** format.  
376  **
377  ** [Expects]
378  **  (char *)img: Name of the image we are requesting.
379  **
380  ** [Returns]
381  **  Success: Ptr to malloc()ed image data.  [*]
382  **  Failure; File not found: NULL
383  **
384  ** (Actually, we will probably want to store this in a temp
385  ** file, and return a pointer to the filename.  Unless you want
386  ** to store a 10-meg image in memory, of course... :)
387  **/
388 char            *CxMiImage(int id, const char *img) {
389
390         /**
391          ** Hmm.. Not sure how similar this is to MESG...
392          ** Will defer this code until I can reference the 
393          ** specs..
394          **/
395
396         return(NULL);
397 }