]> code.citadel.org Git - citadel.git/blob - citadel/citadel_ipc.c
* commands.c: alternate_semantics might have been getting set where it
[citadel.git] / citadel / citadel_ipc.c
1 /* $Id$ */
2
3 #define UDS                     "_UDS_"
4 #define DEFAULT_HOST            UDS
5 #define DEFAULT_PORT            "citadel"
6
7 #include "sysdep.h"
8 #if TIME_WITH_SYS_TIME
9 # include <sys/time.h>
10 # include <time.h>
11 #else
12 # if HAVE_SYS_TIME_H
13 #  include <sys/time.h>
14 # else
15 #  include <time.h>
16 # endif
17 #endif
18 #include <unistd.h>
19 #include <stdio.h>
20 #include <sys/types.h>
21 #include <string.h>
22 #ifdef HAVE_MALLOC_H
23 #include <malloc.h>
24 #endif
25 #include <stdlib.h>
26 #include <ctype.h>
27 #include <sys/socket.h>
28 #include <arpa/inet.h>
29 #include <netinet/in.h>
30 #include <netdb.h>
31 #include <sys/un.h>
32 #include <errno.h>
33 #ifdef THREADED_CLIENT
34 #include <pthread.h>
35 #endif
36 #include "citadel.h"
37 #include "citadel_ipc.h"
38 #include "citadel_decls.h"
39 #include "tools.h"
40
41 #ifdef THREADED_CLIENT
42 pthread_mutex_t rwlock;
43 #endif
44
45 #ifdef HAVE_OPENSSL
46 static SSL_CTX *ssl_ctx;
47 char arg_encrypt;
48 char rc_encrypt;
49 #ifdef THREADED_CLIENT
50 pthread_mutex_t **Critters;                     /* Things that need locking */
51 #endif /* THREADED_CLIENT */
52
53 #endif /* HAVE_OPENSSL */
54
55 #ifndef INADDR_NONE
56 #define INADDR_NONE 0xffffffff
57 #endif
58
59 static void (*status_hook)(char *s) = NULL;
60
61 void setCryptoStatusHook(void (*hook)(char *s)) {
62         status_hook = hook;
63 }
64
65 void CtdlIPC_SetNetworkStatusCallback(CtdlIPC *ipc, void (*hook)(int state)) {
66         ipc->network_status_cb = hook;
67 }
68
69
70 char express_msgs = 0;
71
72
73 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes);
74 static void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes);
75 #ifdef HAVE_OPENSSL
76 static void serv_read_ssl(CtdlIPC *ipc, char *buf, unsigned int bytes);
77 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes);
78 static void ssl_lock(int mode, int n, const char *file, int line);
79 static void endtls(SSL *ssl);
80 #ifdef THREADED_CLIENT
81 static unsigned long id_callback(void);
82 #endif /* THREADED_CLIENT */
83 #endif /* HAVE_OPENSSL */
84
85
86 /*
87  * Does nothing.  The server should always return 200.
88  */
89 int CtdlIPCNoop(CtdlIPC *ipc)
90 {
91         char aaa[128];
92
93         return CtdlIPCGenericCommand(ipc, "NOOP", NULL, 0, NULL, NULL, aaa);
94 }
95
96
97 /*
98  * Does nothing interesting.  The server should always return 200
99  * along with your string.
100  */
101 int CtdlIPCEcho(CtdlIPC *ipc, const char *arg, char *cret)
102 {
103         register int ret;
104         char *aaa;
105         
106         if (!arg) return -2;
107         if (!cret) return -2;
108
109         aaa = (char *)malloc((size_t)(strlen(arg) + 6));
110         if (!aaa) return -1;
111
112         sprintf(aaa, "ECHO %s", arg);
113         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
114         free(aaa);
115         return ret;
116 }
117
118
119 /*
120  * Asks the server to close the connecction.
121  * Should always return 200.
122  */
123 int CtdlIPCQuit(CtdlIPC *ipc)
124 {
125         register int ret;
126         char aaa[128];
127
128         CtdlIPC_lock(ipc);
129         CtdlIPC_putline(ipc, "QUIT");
130         CtdlIPC_getline(ipc, aaa);
131         ret = atoi(aaa);
132         CtdlIPC_unlock(ipc);
133         return ret;
134 }
135
136
137 /*
138  * Asks the server to logout.  Should always return 200, even if no user
139  * was logged in.  The user will not be logged in after this!
140  */
141 int CtdlIPCLogout(CtdlIPC *ipc)
142 {
143         register int ret;
144         char aaa[128];
145
146         CtdlIPC_lock(ipc);
147         CtdlIPC_putline(ipc, "LOUT");
148         CtdlIPC_getline(ipc, aaa);
149         ret = atoi(aaa);
150         CtdlIPC_unlock(ipc);
151         return ret;
152 }
153
154
155 /*
156  * First stage of authentication - pass the username.  Returns 300 if the
157  * username is able to log in, with the username correctly spelled in cret.
158  * Returns various 500 error codes if the user doesn't exist, etc.
159  */
160 int CtdlIPCTryLogin(CtdlIPC *ipc, const char *username, char *cret)
161 {
162         register int ret;
163         char *aaa;
164
165         if (!username) return -2;
166         if (!cret) return -2;
167
168         aaa = (char *)malloc((size_t)(strlen(username) + 6));
169         if (!aaa) return -1;
170
171         sprintf(aaa, "USER %s", username);
172         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
173         free(aaa);
174         return ret;
175 }
176
177
178 /*
179  * Second stage of authentication - provide password.  The server returns
180  * 200 and several arguments in cret relating to the user's account.
181  */
182 int CtdlIPCTryPassword(CtdlIPC *ipc, const char *passwd, char *cret)
183 {
184         register int ret;
185         char *aaa;
186
187         if (!passwd) return -2;
188         if (!cret) return -2;
189
190         aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
191         if (!aaa) return -1;
192
193         sprintf(aaa, "PASS %s", passwd);
194         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
195         free(aaa);
196         return ret;
197 }
198
199
200 /*
201  * Second stage of authentication - provide password.  The server returns
202  * 200 and several arguments in cret relating to the user's account.
203  */
204 int CtdlIPCTryApopPassword(CtdlIPC *ipc, const char *response, char *cret)
205 {
206         register int ret;
207         char *aaa;
208
209         if (!response) return -2;
210         if (!cret) return -2;
211
212         aaa = (char *)malloc((size_t)(strlen(response) + 6));
213         if (!aaa) return -1;
214
215         sprintf(aaa, "PAS2 %s", response);
216         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
217         free(aaa);
218         return ret;
219 }
220
221
222 /*
223  * Create a new user.  This returns 200 plus the same arguments as TryPassword
224  * if selfservice is nonzero, unless there was a problem creating the account.
225  * If selfservice is zero, creates a new user but does not log out the existing
226  * user - intended for use by system administrators to create accounts on
227  * behalf of other users.
228  */
229 int CtdlIPCCreateUser(CtdlIPC *ipc, const char *username, int selfservice, char *cret)
230 {
231         register int ret;
232         char *aaa;
233
234         if (!username) return -2;
235         if (!cret) return -2;
236
237         aaa = (char *)malloc((size_t)(strlen(username) + 6));
238         if (!aaa) return -1;
239
240         sprintf(aaa, "%s %s", selfservice ? "NEWU" : "CREU",  username);
241         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
242         free(aaa);
243         return ret;
244 }
245
246
247 /*
248  * Changes the user's password.  Returns 200 if changed, errors otherwise.
249  */
250 int CtdlIPCChangePassword(CtdlIPC *ipc, const char *passwd, char *cret)
251 {
252         register int ret;
253         char *aaa;
254
255         if (!passwd) return -2;
256         if (!cret) return -2;
257
258         aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
259         if (!aaa) return -1;
260
261         sprintf(aaa, "SETP %s", passwd);
262         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
263         free(aaa);
264         return ret;
265 }
266
267
268 /* LKRN */
269 /* Caller must free the march list */
270 /* Room types are defined in enum RoomList; keep these in sync! */
271 /* floor is -1 for all, or floornum */
272 int CtdlIPCKnownRooms(CtdlIPC *ipc, enum RoomList which, int floor, struct march **listing, char *cret)
273 {
274         register int ret;
275         struct march *march = NULL;
276         static char *proto[] =
277                 {"LKRA", "LKRN", "LKRO", "LZRM", "LRMS", "LPRM" };
278         char aaa[SIZ];
279         char *bbb = NULL;
280         size_t bbbsize;
281
282         if (!listing) return -2;
283         if (*listing) return -2;        /* Free the listing first */
284         if (!cret) return -2;
285         /* if (which < 0 || which > 4) return -2; */
286         if (floor < -1) return -2;      /* Can't validate upper bound, sorry */
287
288         sprintf(aaa, "%s %d", proto[which], floor);
289         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbbsize, cret);
290         if (ret / 100 == 1) {
291                 struct march *mptr;
292
293                 while (bbb && strlen(bbb)) {
294                         int a;
295
296                         extract_token(aaa, bbb, 0, '\n');
297                         a = strlen(aaa);
298                         memmove(bbb, bbb + a + 1, strlen(bbb) - a);
299                         mptr = (struct march *) malloc(sizeof (struct march));
300                         if (mptr) {
301                                 mptr->next = NULL;
302                                 extract(mptr->march_name, aaa, 0);
303                                 mptr->march_flags = (unsigned int) extract_int(aaa, 1);
304                                 mptr->march_floor = (char) extract_int(aaa, 2);
305                                 mptr->march_order = (char) extract_int(aaa, 3);
306                                 if (march == NULL)
307                                         march = mptr;
308                                 else {
309                                         struct march *mptr2;
310
311                                         mptr2 = march;
312                                         while (mptr2->next != NULL)
313                                                 mptr2 = mptr2->next;
314                                         mptr2->next = mptr;
315                                 }
316                         }
317                 }
318         }
319         *listing = march;
320         return ret;
321 }
322
323
324 /* GETU */
325 /* Caller must free the struct usersupp; caller may pass an existing one */
326 int CtdlIPCGetConfig(CtdlIPC *ipc, struct usersupp **uret, char *cret)
327 {
328         register int ret;
329
330         if (!cret) return -2;
331         if (!uret) return -2;
332         if (!*uret) *uret = (struct usersupp *)calloc(1, sizeof (struct usersupp));
333         if (!*uret) return -1;
334
335         ret = CtdlIPCGenericCommand(ipc, "GETU", NULL, 0, NULL, NULL, cret);
336         if (ret / 100 == 2) {
337                 uret[0]->USscreenwidth = extract_int(cret, 0);
338                 uret[0]->USscreenheight = extract_int(cret, 1);
339                 uret[0]->flags = extract_int(cret, 2);
340         }
341         return ret;
342 }
343
344
345 /* SETU */
346 int CtdlIPCSetConfig(CtdlIPC *ipc, struct usersupp *uret, char *cret)
347 {
348         char aaa[48];
349
350         if (!uret) return -2;
351         if (!cret) return -2;
352
353         sprintf(aaa, "SETU %d|%d|%d",
354                         uret->USscreenwidth, uret->USscreenheight,
355                         uret->flags);
356         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
357 }
358
359
360 /* GOTO */
361 int CtdlIPCGotoRoom(CtdlIPC *ipc, const char *room, const char *passwd,
362                 struct ctdlipcroom **rret, char *cret)
363 {
364         register int ret;
365         char *aaa;
366
367         if (!cret) return -2;
368         if (!rret) return -2;
369         if (!*rret) *rret = (struct ctdlipcroom *)calloc(1, sizeof (struct ctdlipcroom));
370         if (!*rret) return -1;
371
372         if (passwd) {
373                 aaa = (char *)malloc(strlen(room) + strlen(passwd) + 7);
374                 if (!aaa) {
375                         free(*rret);
376                         return -1;
377                 }
378                 sprintf(aaa, "GOTO %s|%s", room, passwd);
379         } else {
380                 aaa = (char *)malloc(strlen(room) + 6);
381                 if (!aaa) {
382                         free(*rret);
383                         return -1;
384                 }
385                 sprintf(aaa, "GOTO %s", room);
386         }
387         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
388         if (ret / 100 == 2) {
389                 extract(rret[0]->RRname, cret, 0);
390                 rret[0]->RRunread = extract_long(cret, 1);
391                 rret[0]->RRtotal = extract_long(cret, 2);
392                 rret[0]->RRinfoupdated = extract_int(cret, 3);
393                 rret[0]->RRflags = extract_int(cret, 4);
394                 rret[0]->RRhighest = extract_long(cret, 5);
395                 rret[0]->RRlastread = extract_long(cret, 6);
396                 rret[0]->RRismailbox = extract_int(cret, 7);
397                 rret[0]->RRaide = extract_int(cret, 8);
398                 rret[0]->RRnewmail = extract_long(cret, 9);
399                 rret[0]->RRfloor = extract_int(cret, 10);
400         } else {
401                 free(*rret);
402         }
403         return ret;
404 }
405
406
407 /* MSGS */
408 /* which is 0 = all, 1 = old, 2 = new, 3 = last, 4 = first, 5 = gt, 6 = lt */
409 /* whicharg is number of messages, applies to last, first, gt, lt */
410 int CtdlIPCGetMessages(CtdlIPC *ipc, enum MessageList which, int whicharg,
411                 const char *mtemplate, unsigned long **mret, char *cret)
412 {
413         register int ret;
414         register unsigned long count = 0;
415         static char *proto[] =
416                 { "ALL", "OLD", "NEW", "LAST", "FIRST", "GT", "LT" };
417         char aaa[33];
418         char *bbb = NULL;
419         size_t bbbsize;
420
421         if (!cret) return -2;
422         if (!mret) return -2;
423         if (*mret) return -2;
424         if (which < 0 || which > 6) return -2;
425
426         if (which <= 2)
427                 sprintf(aaa, "MSGS %s||%d", proto[which],
428                                 (mtemplate) ? 1 : 0);
429         else
430                 sprintf(aaa, "MSGS %s|%d|%d", proto[which], whicharg,
431                                 (mtemplate) ? 1 : 0);
432         if (mtemplate) count = strlen(mtemplate);
433         ret = CtdlIPCGenericCommand(ipc, aaa, mtemplate, count, &bbb, &bbbsize, cret);
434         if (ret / 100 != 1)
435                 return ret;
436         count = 0;
437         *mret = (unsigned long *)calloc(1, sizeof(unsigned long));
438         if (!*mret)
439                 return -1;
440         while (bbb && strlen(bbb)) {
441                 extract_token(aaa, bbb, 0, '\n');
442                 remove_token(bbb, 0, '\n');
443                 *mret = (unsigned long *)realloc(*mret, (size_t)((count + 2) *
444                                         sizeof (unsigned long)));
445                 if (*mret) {
446                         (*mret)[count++] = atol(aaa);
447                         (*mret)[count] = 0L;
448                 } else {
449                         break;
450                 }
451         }
452         if (bbb) free(bbb);
453         return ret;
454 }
455
456
457 /* MSG0, MSG2 */
458 int CtdlIPCGetSingleMessage(CtdlIPC *ipc, long msgnum, int headers, int as_mime,
459                 struct ctdlipcmessage **mret, char *cret)
460 {
461         register int ret;
462         char aaa[SIZ];
463         char *bbb = NULL;
464         size_t bbbsize;
465         int multipart_hunting = 0;
466         char multipart_prefix[SIZ];
467
468         if (!cret) return -1;
469         if (!mret) return -1;
470         if (!*mret) *mret = (struct ctdlipcmessage *)calloc(1, sizeof (struct ctdlipcmessage));
471         if (!*mret) return -1;
472         if (!msgnum) return -1;
473
474         strcpy(mret[0]->content_type, "");
475         sprintf(aaa, "MSG%d %ld|%d", as_mime, msgnum, headers);
476         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbbsize, cret);
477         if (ret / 100 == 1) {
478                 if (as_mime != 2) {
479                         strcpy(mret[0]->mime_chosen, "1");      /* Default chosen-part is "1" */
480                         while (strlen(bbb) > 4 && bbb[4] == '=') {
481                                 extract_token(aaa, bbb, 0, '\n');
482                                 remove_token(bbb, 0, '\n');
483
484                                 if (!strncasecmp(aaa, "nhdr=yes", 8))
485                                         mret[0]->nhdr = 1;
486                                 else if (!strncasecmp(aaa, "from=", 5))
487                                         strcpy(mret[0]->author, &aaa[5]);
488                                 else if (!strncasecmp(aaa, "type=", 5))
489                                         mret[0]->type = atoi(&aaa[5]);
490                                 else if (!strncasecmp(aaa, "msgn=", 5))
491                                         strcpy(mret[0]->msgid, &aaa[5]);
492                                 else if (!strncasecmp(aaa, "subj=", 5))
493                                         strcpy(mret[0]->subject, &aaa[5]);
494                                 else if (!strncasecmp(aaa, "rfca=", 5))
495                                         strcpy(mret[0]->email, &aaa[5]);
496                                 else if (!strncasecmp(aaa, "hnod=", 5))
497                                         strcpy(mret[0]->hnod, &aaa[5]);
498                                 else if (!strncasecmp(aaa, "room=", 5))
499                                         strcpy(mret[0]->room, &aaa[5]);
500                                 else if (!strncasecmp(aaa, "node=", 5))
501                                         strcpy(mret[0]->node, &aaa[5]);
502                                 else if (!strncasecmp(aaa, "rcpt=", 5))
503                                         strcpy(mret[0]->recipient, &aaa[5]);
504                                 else if (!strncasecmp(aaa, "time=", 5))
505                                         mret[0]->time = atol(&aaa[5]);
506
507                                 /* Multipart/alternative prefix & suffix strings help
508                                  * us to determine which part we want to download.
509                                  */
510                                 else if (!strncasecmp(aaa, "pref=", 5)) {
511                                         extract(multipart_prefix, &aaa[5], 1);
512                                         if (!strcasecmp(multipart_prefix,
513                                            "multipart/alternative")) {
514                                                 ++multipart_hunting;
515                                         }
516                                 }
517                                 else if (!strncasecmp(aaa, "suff=", 5)) {
518                                         extract(multipart_prefix, &aaa[5], 1);
519                                         if (!strcasecmp(multipart_prefix,
520                                            "multipart/alternative")) {
521                                                 ++multipart_hunting;
522                                         }
523                                 }
524
525                                 else if (!strncasecmp(aaa, "part=", 5)) {
526                                         struct parts *ptr, *chain;
527         
528                                         ptr = (struct parts *)calloc(1, sizeof (struct parts));
529                                         if (ptr) {
530
531                                                 /* Fill the buffers for the caller */
532                                                 extract(ptr->name, &aaa[5], 0);
533                                                 extract(ptr->filename, &aaa[5], 1);
534                                                 extract(ptr->number, &aaa[5], 2);
535                                                 extract(ptr->disposition, &aaa[5], 3);
536                                                 extract(ptr->mimetype, &aaa[5], 4);
537                                                 ptr->length = extract_long(&aaa[5], 5);
538                                                 if (!mret[0]->attachments)
539                                                         mret[0]->attachments = ptr;
540                                                 else {
541                                                         chain = mret[0]->attachments;
542                                                         while (chain->next)
543                                                                 chain = chain->next;
544                                                         chain->next = ptr;
545                                                 }
546
547                                                 /* Now handle multipart/alternative */
548                                                 if (multipart_hunting > 0) {
549                                                         if ( (!strcasecmp(ptr->mimetype,
550                                                              "text/plain"))
551                                                            || (!strcasecmp(ptr->mimetype,
552                                                               "text/html")) ) {
553                                                                 strcpy(mret[0]->mime_chosen,
554                                                                         ptr->number);
555                                                         }
556                                                 }
557
558                                         }
559                                 }
560                         }
561                         /* Eliminate "text\n" */
562                         remove_token(bbb, 0, '\n');
563
564                         /* If doing a MIME thing, pull out the extra headers */
565                         if (as_mime == 4) {
566                                 do {
567                                         if (!strncasecmp(bbb, "Content-type: ", 14)) {
568                                                 extract_token(mret[0]->content_type, bbb, 0, '\n');
569                                                 strcpy(mret[0]->content_type,
570                                                         &mret[0]->content_type[14]);
571                                                 striplt(mret[0]->content_type);
572                                         }
573                                         remove_token(bbb, 0, '\n');
574                                 } while ((bbb[0] != 0) && (bbb[0] != '\n'));
575                 /*ooga*/                remove_token(bbb, 0, '\n');
576                         }
577
578
579                 }
580                 if (strlen(bbb)) {
581                         /* FIXME: Strip trailing whitespace */
582                         bbb = (char *)realloc(bbb, (size_t)(strlen(bbb) + 1));
583                 } else {
584                         bbb = (char *)realloc(bbb, 1);
585                         *bbb = '\0';
586                 }
587                 mret[0]->text = bbb;
588         }
589         return ret;
590 }
591
592
593 /* WHOK */
594 int CtdlIPCWhoKnowsRoom(CtdlIPC *ipc, char **listing, char *cret)
595 {
596         register int ret;
597         size_t bytes;
598
599         if (!cret) return -2;
600         if (!listing) return -2;
601         if (*listing) return -2;
602
603         ret = CtdlIPCGenericCommand(ipc, "WHOK", NULL, 0, listing, &bytes, cret);
604         return ret;
605 }
606
607
608 /* INFO */
609 int CtdlIPCServerInfo(CtdlIPC *ipc, struct CtdlServInfo *ServInfo, char *cret)
610 {
611         register int ret;
612         size_t bytes;
613         char *listing = NULL;
614         char buf[SIZ];
615
616         if (!cret) return -2;
617         if (!ServInfo) return -2;
618
619         ret = CtdlIPCGenericCommand(ipc, "INFO", NULL, 0, &listing, &bytes, cret);
620         if (ret / 100 == 1) {
621                 int line = 0;
622
623                 while (*listing && strlen(listing)) {
624                         extract_token(buf, listing, 0, '\n');
625                         remove_token(listing, 0, '\n');
626                         switch (line++) {
627                         case 0:         ServInfo->serv_pid = atoi(buf);
628                                         break;
629                         case 1:         strcpy(ServInfo->serv_nodename,buf);
630                                         break;
631                         case 2:         strcpy(ServInfo->serv_humannode,buf);
632                                         break;
633                         case 3:         strcpy(ServInfo->serv_fqdn,buf);
634                                         break;
635                         case 4:         strcpy(ServInfo->serv_software,buf);
636                                         break;
637                         case 5:         ServInfo->serv_rev_level = atoi(buf);
638                                         break;
639                         case 6:         strcpy(ServInfo->serv_bbs_city,buf);
640                                         break;
641                         case 7:         strcpy(ServInfo->serv_sysadm,buf);
642                                         break;
643                         case 9:         strcpy(ServInfo->serv_moreprompt,buf);
644                                         break;
645                         case 10:        ServInfo->serv_ok_floors = atoi(buf);
646                                         break;
647                         case 11:        ServInfo->serv_paging_level = atoi(buf);
648                                         break;
649                         case 13:        ServInfo->serv_supports_qnop = atoi(buf);
650                                         break;
651                         }
652                 }
653
654         }
655         return ret;
656 }
657
658
659 /* RDIR */
660 int CtdlIPCReadDirectory(CtdlIPC *ipc, char **listing, char *cret)
661 {
662         register int ret;
663         size_t bytes;
664
665         if (!cret) return -2;
666         if (!listing) return -2;
667         if (*listing) return -2;
668
669         ret = CtdlIPCGenericCommand(ipc, "RDIR", NULL, 0, listing, &bytes, cret);
670         return ret;
671 }
672
673
674 /*
675  * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
676  */
677 int CtdlIPCSetLastRead(CtdlIPC *ipc, long msgnum, char *cret)
678 {
679         register int ret;
680         char aaa[16];
681
682         if (!cret) return -2;
683
684         if (msgnum) {
685                 sprintf(aaa, "SLRP %ld", msgnum);
686         }
687         else {
688                 sprintf(aaa, "SLRP HIGHEST");
689         }
690         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
691         return ret;
692 }
693
694
695 /* INVT */
696 int CtdlIPCInviteUserToRoom(CtdlIPC *ipc, const char *username, char *cret)
697 {
698         register int ret;
699         char *aaa;
700
701         if (!cret) return -2;
702         if (!username) return -2;
703
704         aaa = (char *)malloc(strlen(username) + 6);
705         if (!aaa) return -1;
706
707         sprintf(aaa, "INVT %s", username);
708         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
709         free(aaa);
710         return ret;
711 }
712
713
714 /* KICK */
715 int CtdlIPCKickoutUserFromRoom(CtdlIPC *ipc, const char *username, char *cret)
716 {
717         register int ret;
718         char *aaa;
719
720         if (!cret) return -1;
721         if (!username) return -1;
722
723         aaa = (char *)malloc(strlen(username) + 6);
724
725         sprintf(aaa, "KICK %s", username);
726         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
727         free(aaa);
728         return ret;
729 }
730
731
732 /* GETR */
733 int CtdlIPCGetRoomAttributes(CtdlIPC *ipc, struct quickroom **qret, char *cret)
734 {
735         register int ret;
736
737         if (!cret) return -2;
738         if (!qret) return -2;
739         if (!*qret) *qret = (struct quickroom *)calloc(1, sizeof (struct quickroom));
740         if (!*qret) return -1;
741
742         ret = CtdlIPCGenericCommand(ipc, "GETR", NULL, 0, NULL, NULL, cret);
743         if (ret / 100 == 2) {
744                 extract(qret[0]->QRname, cret, 0);
745                 extract(qret[0]->QRpasswd, cret, 1);
746                 extract(qret[0]->QRdirname, cret, 2);
747                 qret[0]->QRflags = extract_int(cret, 3);
748                 qret[0]->QRfloor = extract_int(cret, 4);
749                 qret[0]->QRorder = extract_int(cret, 5);
750                 qret[0]->QRdefaultview = extract_int(cret, 6);
751                 qret[0]->QRflags2 = extract_int(cret, 7);
752         }
753         return ret;
754 }
755
756
757 /* SETR */
758 /* set forget to kick all users out of room */
759 int CtdlIPCSetRoomAttributes(CtdlIPC *ipc, int forget, struct quickroom *qret, char *cret)
760 {
761         register int ret;
762         char *aaa;
763
764         if (!cret) return -2;
765         if (!qret) return -2;
766
767         aaa = (char *)malloc(strlen(qret->QRname) + strlen(qret->QRpasswd) +
768                         strlen(qret->QRdirname) + 52);
769         if (!aaa) return -1;
770
771         sprintf(aaa, "SETR %s|%s|%s|%d|%d|%d|%d",
772                         qret->QRname, qret->QRpasswd, qret->QRdirname,
773                         qret->QRflags, forget, qret->QRfloor, qret->QRorder);
774         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
775         free(aaa);
776         return ret;
777 }
778
779
780 /* GETA */
781 int CtdlIPCGetRoomAide(CtdlIPC *ipc, char *cret)
782 {
783         if (!cret) return -1;
784
785         return CtdlIPCGenericCommand(ipc, "GETA", NULL, 0, NULL, NULL, cret);
786 }
787
788
789 /* SETA */
790 int CtdlIPCSetRoomAide(CtdlIPC *ipc, const char *username, char *cret)
791 {
792         register int ret;
793         char *aaa;
794
795         if (!cret) return -2;
796         if (!username) return -2;
797
798         aaa = (char *)malloc(strlen(username) + 6);
799         if (!aaa) return -1;
800
801         sprintf(aaa, "SETA %s", username);
802         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
803         free(aaa);
804         return ret;
805 }
806
807
808 /* ENT0 */
809 int CtdlIPCPostMessage(CtdlIPC *ipc, int flag, const struct ctdlipcmessage *mr, char *cret)
810 {
811         register int ret;
812         char *aaa;
813
814         if (!cret) return -2;
815         if (!mr) return -2;
816
817         aaa = (char *)malloc(strlen(mr->recipient) + strlen(mr->author) + 40);
818         if (!aaa) return -1;
819
820         sprintf(aaa, "ENT0 %d|%s|%d|%d|%s|%s", flag, mr->recipient,
821                         mr->anonymous, mr->type, mr->subject, mr->author);
822         ret = CtdlIPCGenericCommand(ipc, aaa, mr->text, strlen(mr->text), NULL,
823                         NULL, cret);
824         free(aaa);
825         return ret;
826 }
827
828
829 /* RINF */
830 int CtdlIPCRoomInfo(CtdlIPC *ipc, char **iret, char *cret)
831 {
832         size_t bytes;
833
834         if (!cret) return -2;
835         if (!iret) return -2;
836         if (*iret) return -2;
837
838         return CtdlIPCGenericCommand(ipc, "RINF", NULL, 0, iret, &bytes, cret);
839 }
840
841
842 /* DELE */
843 int CtdlIPCDeleteMessage(CtdlIPC *ipc, long msgnum, char *cret)
844 {
845         char aaa[16];
846
847         if (!cret) return -2;
848         if (!msgnum) return -2;
849
850         sprintf(aaa, "DELE %ld", msgnum);
851         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
852 }
853
854
855 /* MOVE */
856 int CtdlIPCMoveMessage(CtdlIPC *ipc, int copy, long msgnum, const char *destroom, char *cret)
857 {
858         register int ret;
859         char *aaa;
860
861         if (!cret) return -2;
862         if (!destroom) return -2;
863         if (!msgnum) return -2;
864
865         aaa = (char *)malloc(strlen(destroom) + 28);
866         if (!aaa) return -1;
867
868         sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
869         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
870         free(aaa);
871         return ret;
872 }
873
874
875 /* KILL */
876 int CtdlIPCDeleteRoom(CtdlIPC *ipc, int for_real, char *cret)
877 {
878         char aaa[16];
879
880         if (!cret) return -2;
881
882         sprintf(aaa, "KILL %d", for_real);
883         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
884 }
885
886
887 /* CRE8 */
888 int CtdlIPCCreateRoom(CtdlIPC *ipc, int for_real, const char *roomname, int type,
889                 const char *password, int floor, char *cret)
890 {
891         register int ret;
892         char *aaa;
893
894         if (!cret) return -2;
895         if (!roomname) return -2;
896
897         if (password) {
898                 aaa = (char *)malloc(strlen(roomname) + strlen(password) + 40);
899                 if (!aaa) return -1;
900                 sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type,
901                                 password, floor);
902         } else {
903                 aaa = (char *)malloc(strlen(roomname) + 40);
904                 if (!aaa) return -1;
905                 sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type,
906                                 floor);
907         }
908         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
909         free(aaa);
910         return ret;
911 }
912
913
914 /* FORG */
915 int CtdlIPCForgetRoom(CtdlIPC *ipc, char *cret)
916 {
917         if (!cret) return -2;
918
919         return CtdlIPCGenericCommand(ipc, "FORG", NULL, 0, NULL, NULL, cret);
920 }
921
922
923 /* MESG */
924 int CtdlIPCSystemMessage(CtdlIPC *ipc, const char *message, char **mret, char *cret)
925 {
926         register int ret;
927         char *aaa;
928         size_t bytes;
929
930         if (!cret) return -2;
931         if (!mret) return -2;
932         if (*mret) return -2;
933         if (!message) return -2;
934
935         aaa = (char *)malloc(strlen(message) + 6);
936         if (!aaa) return -1;
937
938         sprintf(aaa, "MESG %s", message);
939         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, mret, &bytes, cret);
940         free(aaa);
941         return ret;
942 }
943
944
945 /* GNUR */
946 int CtdlIPCNextUnvalidatedUser(CtdlIPC *ipc, char *cret)
947 {
948         if (!cret) return -2;
949
950         return CtdlIPCGenericCommand(ipc, "GNUR", NULL, 0, NULL, NULL, cret);
951 }
952
953
954 /* GREG */
955 int CtdlIPCGetUserRegistration(CtdlIPC *ipc, const char *username, char **rret, char *cret)
956 {
957         register int ret;
958         char *aaa;
959         size_t bytes;
960
961         if (!cret) return -2;
962         if (!rret) return -2;
963         if (*rret) return -2;
964
965         if (username)
966                 aaa = (char *)malloc(strlen(username) + 6);
967         else
968                 aaa = (char *)malloc(12);
969         if (!aaa) return -1;
970
971         if (username)
972                 sprintf(aaa, "GREG %s", username);
973         else
974                 sprintf(aaa, "GREG _SELF_");
975         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, rret, &bytes, cret);
976         free(aaa);
977         return ret;
978 }
979
980
981 /* VALI */
982 int CtdlIPCValidateUser(CtdlIPC *ipc, const char *username, int axlevel, char *cret)
983 {
984         register int ret;
985         char *aaa;
986
987         if (!cret) return -2;
988         if (!username) return -2;
989         if (axlevel < 0 || axlevel > 7) return -2;
990
991         aaa = (char *)malloc(strlen(username) + 17);
992         if (!aaa) return -1;
993
994         sprintf(aaa, "VALI %s|%d", username, axlevel);
995         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
996         free(aaa);
997         return ret;
998 }
999
1000
1001 /* EINF */
1002 int CtdlIPCSetRoomInfo(CtdlIPC *ipc, int for_real, const char *info, char *cret)
1003 {
1004         char aaa[16];
1005
1006         if (!cret) return -1;
1007         if (!info) return -1;
1008
1009         sprintf(aaa, "EINF %d", for_real);
1010         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1011 }
1012
1013
1014 /* LIST */
1015 int CtdlIPCUserListing(CtdlIPC *ipc, char **listing, char *cret)
1016 {
1017         size_t bytes;
1018
1019         if (!cret) return -1;
1020         if (!listing) return -1;
1021         if (*listing) return -1;
1022
1023         return CtdlIPCGenericCommand(ipc, "LIST", NULL, 0, listing, &bytes, cret);
1024 }
1025
1026
1027 /* REGI */
1028 int CtdlIPCSetRegistration(CtdlIPC *ipc, const char *info, char *cret)
1029 {
1030         if (!cret) return -1;
1031         if (!info) return -1;
1032
1033         return CtdlIPCGenericCommand(ipc, "REGI", info, strlen(info),
1034                         NULL, NULL, cret);
1035 }
1036
1037
1038 /* CHEK */
1039 int CtdlIPCMiscCheck(CtdlIPC *ipc, struct ctdlipcmisc *chek, char *cret)
1040 {
1041         register int ret;
1042
1043         if (!cret) return -1;
1044         if (!chek) return -1;
1045
1046         ret = CtdlIPCGenericCommand(ipc, "CHEK", NULL, 0, NULL, NULL, cret);
1047         if (ret / 100 == 2) {
1048                 chek->newmail = extract_long(cret, 0);
1049                 chek->needregis = extract_int(cret, 1);
1050                 chek->needvalid = extract_int(cret, 2);
1051         }
1052         return ret;
1053 }
1054
1055
1056 /* DELF */
1057 int CtdlIPCDeleteFile(CtdlIPC *ipc, const char *filename, char *cret)
1058 {
1059         register int ret;
1060         char *aaa;
1061
1062         if (!cret) return -2;
1063         if (!filename) return -2;
1064         
1065         aaa = (char *)malloc(strlen(filename) + 6);
1066         if (!aaa) return -1;
1067
1068         sprintf(aaa, "DELF %s", filename);
1069         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1070         free(aaa);
1071         return ret;
1072 }
1073
1074
1075 /* MOVF */
1076 int CtdlIPCMoveFile(CtdlIPC *ipc, const char *filename, const char *destroom, char *cret)
1077 {
1078         register int ret;
1079         char *aaa;
1080
1081         if (!cret) return -2;
1082         if (!filename) return -2;
1083         if (!destroom) return -2;
1084
1085         aaa = (char *)malloc(strlen(filename) + strlen(destroom) + 7);
1086         if (!aaa) return -1;
1087
1088         sprintf(aaa, "MOVF %s|%s", filename, destroom);
1089         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1090         free(aaa);
1091         return ret;
1092 }
1093
1094
1095 /* NETF */
1096 int CtdlIPCNetSendFile(CtdlIPC *ipc, const char *filename, const char *destnode, char *cret)
1097 {
1098         register int ret;
1099         char *aaa;
1100
1101         if (!cret) return -2;
1102         if (!filename) return -2;
1103         if (!destnode) return -2;
1104
1105         aaa = (char *)malloc(strlen(filename) + strlen(destnode) + 7);
1106         if (!aaa) return -1;
1107
1108         sprintf(aaa, "NETF %s|%s", filename, destnode);
1109         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1110         free(aaa);
1111         return ret;
1112 }
1113
1114
1115 /* RWHO */
1116 int CtdlIPCOnlineUsers(CtdlIPC *ipc, char **listing, time_t *stamp, char *cret)
1117 {
1118         register int ret;
1119         size_t bytes;
1120
1121         if (!cret) return -1;
1122         if (!listing) return -1;
1123         if (*listing) return -1;
1124
1125         *stamp = CtdlIPCServerTime(ipc, cret);
1126         if (!*stamp)
1127                 *stamp = time(NULL);
1128         ret = CtdlIPCGenericCommand(ipc, "RWHO", NULL, 0, listing, &bytes, cret);
1129         return ret;
1130 }
1131
1132
1133 /* OPEN */
1134 int CtdlIPCFileDownload(CtdlIPC *ipc, const char *filename, void **buf,
1135                 size_t resume,
1136                 void (*progress_gauge_callback)(unsigned long, unsigned long),
1137                 char *cret)
1138 {
1139         register int ret;
1140         size_t bytes;
1141         time_t last_mod;
1142         char mimetype[SIZ];
1143         char *aaa;
1144
1145         if (!cret) return -2;
1146         if (!filename) return -2;
1147         if (!buf) return -2;
1148         if (*buf) return -2;
1149         if (ipc->downloading) return -2;
1150
1151         aaa = (char *)malloc(strlen(filename) + 6);
1152         if (!aaa) return -1;
1153
1154         sprintf(aaa, "OPEN %s", filename);
1155         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1156         free(aaa);
1157         if (ret / 100 == 2) {
1158                 ipc->downloading = 1;
1159                 bytes = extract_long(cret, 0);
1160                 last_mod = extract_int(cret, 1);
1161                 extract(mimetype, cret, 2);
1162 /*              ret = CtdlIPCReadDownload(ipc, buf, bytes, resume, progress_gauge_callback, cret); */
1163                 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, resume, progress_gauge_callback, cret);
1164                 ret = CtdlIPCEndDownload(ipc, cret);
1165                 if (ret / 100 == 2)
1166                         sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1167                                         filename, mimetype);
1168         }
1169         return ret;
1170 }
1171
1172
1173 /* OPNA */
1174 int CtdlIPCAttachmentDownload(CtdlIPC *ipc, long msgnum, const char *part,
1175                 void **buf,
1176                 void (*progress_gauge_callback)(unsigned long, unsigned long),
1177                 char *cret)
1178 {
1179         register int ret;
1180         size_t bytes;
1181         time_t last_mod;
1182         char filename[SIZ];
1183         char mimetype[SIZ];
1184         char *aaa;
1185
1186         if (!cret) return -2;
1187         if (!buf) return -2;
1188         if (*buf) return -2;
1189         if (!part) return -2;
1190         if (!msgnum) return -2;
1191         if (ipc->downloading) return -2;
1192
1193         aaa = (char *)malloc(strlen(part) + 17);
1194         if (!aaa) return -1;
1195
1196         sprintf(aaa, "OPNA %ld|%s", msgnum, part);
1197         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1198         free(aaa);
1199         if (ret / 100 == 2) {
1200                 ipc->downloading = 1;
1201                 bytes = extract_long(cret, 0);
1202                 last_mod = extract_int(cret, 1);
1203                 extract(mimetype, cret, 2);
1204 /*              ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1205                 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1206                 ret = CtdlIPCEndDownload(ipc, cret);
1207                 if (ret / 100 == 2)
1208                         sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1209                                         filename, mimetype);
1210         }
1211         return ret;
1212 }
1213
1214
1215 /* OIMG */
1216 int CtdlIPCImageDownload(CtdlIPC *ipc, const char *filename, void **buf,
1217                 void (*progress_gauge_callback)(unsigned long, unsigned long),
1218                 char *cret)
1219 {
1220         register int ret;
1221         size_t bytes;
1222         time_t last_mod;
1223         char mimetype[SIZ];
1224         char *aaa;
1225
1226         if (!cret) return -1;
1227         if (!buf) return -1;
1228         if (*buf) return -1;
1229         if (!filename) return -1;
1230         if (ipc->downloading) return -1;
1231
1232         aaa = (char *)malloc(strlen(filename) + 6);
1233         if (!aaa) return -1;
1234
1235         sprintf(aaa, "OIMG %s", filename);
1236         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1237         free(aaa);
1238         if (ret / 100 == 2) {
1239                 ipc->downloading = 1;
1240                 bytes = extract_long(cret, 0);
1241                 last_mod = extract_int(cret, 1);
1242                 extract(mimetype, cret, 2);
1243 /*              ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1244                 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1245                 ret = CtdlIPCEndDownload(ipc, cret);
1246                 if (ret / 100 == 2)
1247                         sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1248                                         filename, mimetype);
1249         }
1250         return ret;
1251 }
1252
1253
1254 /* UOPN */
1255 int CtdlIPCFileUpload(CtdlIPC *ipc, const char *save_as, const char *comment,
1256                 const char *path,
1257                 void (*progress_gauge_callback)(unsigned long, unsigned long),
1258                 char *cret)
1259 {
1260         register int ret;
1261         char *aaa;
1262
1263         if (!cret) return -1;
1264         if (!save_as) return -1;
1265         if (!comment) return -1;
1266         if (!path) return -1;
1267         if (!*path) return -1;
1268         if (ipc->uploading) return -1;
1269
1270         aaa = (char *)malloc(strlen(save_as) + strlen(comment) + 7);
1271         if (!aaa) return -1;
1272
1273         sprintf(aaa, "UOPN %s|%s", save_as, comment);
1274         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1275         free(aaa);
1276         if (ret / 100 == 2) {
1277                 ipc->uploading = 1;
1278                 ret = CtdlIPCWriteUpload(ipc, path, progress_gauge_callback, cret);
1279                 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1280                 ipc->uploading = 0;
1281         }
1282         return ret;
1283 }
1284
1285
1286 /* UIMG */
1287 int CtdlIPCImageUpload(CtdlIPC *ipc, int for_real, const char *path,
1288                 const char *save_as,
1289                 void (*progress_gauge_callback)(unsigned long, unsigned long),
1290                 char *cret)
1291 {
1292         register int ret;
1293         char *aaa;
1294
1295         if (!cret) return -1;
1296         if (!save_as) return -1;
1297         if (!path && for_real) return -1;
1298         if (!*path && for_real) return -1;
1299         if (ipc->uploading) return -1;
1300
1301         aaa = (char *)malloc(strlen(save_as) + 17);
1302         if (!aaa) return -1;
1303
1304         sprintf(aaa, "UIMG %d|%s", for_real, save_as);
1305         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1306         free(aaa);
1307         if (ret / 100 == 2 && for_real) {
1308                 ipc->uploading = 1;
1309                 ret = CtdlIPCWriteUpload(ipc, path, progress_gauge_callback, cret);
1310                 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1311                 ipc->uploading = 0;
1312         }
1313         return ret;
1314 }
1315
1316
1317 /* QUSR */
1318 int CtdlIPCQueryUsername(CtdlIPC *ipc, const char *username, char *cret)
1319 {
1320         register int ret;
1321         char *aaa;
1322
1323         if (!cret) return -2;
1324         if (!username) return -2;
1325
1326         aaa = (char *)malloc(strlen(username) + 6);
1327         if (!aaa) return -1;
1328
1329         sprintf(aaa, "QUSR %s", username);
1330         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1331         free(aaa);
1332         return ret;
1333 }
1334
1335
1336 /* LFLR */
1337 int CtdlIPCFloorListing(CtdlIPC *ipc, char **listing, char *cret)
1338 {
1339         size_t bytes;
1340
1341         if (!cret) return -2;
1342         if (!listing) return -2;
1343         if (*listing) return -2;
1344
1345         return CtdlIPCGenericCommand(ipc, "LFLR", NULL, 0, listing, &bytes, cret);
1346 }
1347
1348
1349 /* CFLR */
1350 int CtdlIPCCreateFloor(CtdlIPC *ipc, int for_real, const char *name, char *cret)
1351 {
1352         register int ret;
1353         char *aaa;
1354
1355         if (!cret) return -2;
1356         if (!name) return -2;
1357         if (!*name) return -2;
1358
1359         aaa = (char *)malloc(strlen(name) + 17);
1360         if (!aaa) return -1;
1361
1362         sprintf(aaa, "CFLR %s|%d", name, for_real);
1363         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1364         free(aaa);
1365         return ret;
1366 }
1367
1368
1369 /* KFLR */
1370 int CtdlIPCDeleteFloor(CtdlIPC *ipc, int for_real, int floornum, char *cret)
1371 {
1372         char aaa[27];
1373
1374         if (!cret) return -1;
1375         if (floornum < 0) return -1;
1376
1377         sprintf(aaa, "KFLR %d|%d", floornum, for_real);
1378         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1379 }
1380
1381
1382 /* EFLR */
1383 int CtdlIPCEditFloor(CtdlIPC *ipc, int floornum, const char *floorname, char *cret)
1384 {
1385         register int ret;
1386         char *aaa;
1387
1388         if (!cret) return -2;
1389         if (!floorname) return -2;
1390         if (floornum < 0) return -2;
1391
1392         aaa = (char *)malloc(strlen(floorname) + 17);
1393         if (!aaa) return -1;
1394
1395         sprintf(aaa, "EFLR %d|%s", floornum, floorname);
1396         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1397         free(aaa);
1398         return ret;
1399 }
1400
1401
1402 /*
1403  * IDEN 
1404  *
1405  * You only need to fill out hostname, the defaults will be used if any of the
1406  * other fields are not set properly.
1407  */
1408 int CtdlIPCIdentifySoftware(CtdlIPC *ipc, int developerid, int clientid,
1409                 int revision, const char *software_name, const char *hostname,
1410                 char *cret)
1411 {
1412         register int ret;
1413         char *aaa;
1414
1415         if (developerid < 0 || clientid < 0 || revision < 0 ||
1416             !software_name) {
1417                 developerid = 8;
1418                 clientid = 0;
1419                 revision = REV_LEVEL - 600;
1420                 software_name = "Citadel/UX (libcitadel)";
1421         }
1422         if (!hostname) return -2;
1423
1424         aaa = (char *)malloc(strlen(software_name) + strlen(hostname) + 29);
1425         if (!aaa) return -1;
1426
1427         sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid,
1428                         revision, software_name, hostname);
1429         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1430         free(aaa);
1431         return ret;
1432 }
1433
1434
1435 /* SEXP */
1436 int CtdlIPCSendInstantMessage(CtdlIPC *ipc, const char *username, const char *text,
1437                 char *cret)
1438 {
1439         register int ret;
1440         char *aaa;
1441
1442         if (!cret) return -2;
1443         if (!username) return -2;
1444
1445         aaa = (char *)malloc(strlen(username) + 8);
1446         if (!aaa) return -1;
1447
1448         if (text) {
1449                 sprintf(aaa, "SEXP %s|-", username);
1450                 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text),
1451                                 NULL, NULL, cret);
1452         } else {
1453                 sprintf(aaa, "SEXP %s||", username);
1454                 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1455         }
1456         free(aaa);
1457         return ret;
1458 }
1459
1460
1461 /* GEXP */
1462 int CtdlIPCGetInstantMessage(CtdlIPC *ipc, char **listing, char *cret)
1463 {
1464         size_t bytes;
1465
1466         if (!cret) return -2;
1467         if (!listing) return -2;
1468         if (*listing) return -2;
1469
1470         return CtdlIPCGenericCommand(ipc, "GEXP", NULL, 0, listing, &bytes, cret);
1471 }
1472
1473
1474 /* DEXP */
1475 /* mode is 0 = enable, 1 = disable, 2 = status */
1476 int CtdlIPCEnableInstantMessageReceipt(CtdlIPC *ipc, int mode, char *cret)
1477 {
1478         char aaa[16];
1479
1480         if (!cret) return -2;
1481
1482         sprintf(aaa, "DEXP %d", mode);
1483         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1484 }
1485
1486
1487 /* EBIO */
1488 int CtdlIPCSetBio(CtdlIPC *ipc, char *bio, char *cret)
1489 {
1490         if (!cret) return -2;
1491         if (!bio) return -2;
1492
1493         return CtdlIPCGenericCommand(ipc, "EBIO", bio, strlen(bio),
1494                         NULL, NULL, cret);
1495 }
1496
1497
1498 /* RBIO */
1499 int CtdlIPCGetBio(CtdlIPC *ipc, const char *username, char **listing, char *cret)
1500 {
1501         register int ret;
1502         size_t bytes;
1503         char *aaa;
1504
1505         if (!cret) return -2;
1506         if (!username) return -2;
1507         if (!listing) return -2;
1508         if (*listing) return -2;
1509
1510         aaa = (char *)malloc(strlen(username) + 6);
1511         if (!aaa) return -1;
1512
1513         sprintf(aaa, "RBIO %s", username);
1514         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
1515         free(aaa);
1516         return ret;
1517 }
1518
1519
1520 /* LBIO */
1521 int CtdlIPCListUsersWithBios(CtdlIPC *ipc, char **listing, char *cret)
1522 {
1523         size_t bytes;
1524
1525         if (!cret) return -2;
1526         if (!listing) return -2;
1527         if (*listing) return -2;
1528
1529         return CtdlIPCGenericCommand(ipc, "LBIO", NULL, 0, listing, &bytes, cret);
1530 }
1531
1532
1533 /* STEL */
1534 int CtdlIPCStealthMode(CtdlIPC *ipc, int mode, char *cret)
1535 {
1536         char aaa[16];
1537
1538         if (!cret) return -1;
1539
1540         sprintf(aaa, "STEL %d", mode ? 1 : 0);
1541         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1542 }
1543
1544
1545 /* TERM */
1546 int CtdlIPCTerminateSession(CtdlIPC *ipc, int sid, char *cret)
1547 {
1548         char aaa[16];
1549
1550         if (!cret) return -1;
1551
1552         sprintf(aaa, "TERM %d", sid);
1553         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1554 }
1555
1556
1557 /* DOWN */
1558 int CtdlIPCTerminateServerNow(CtdlIPC *ipc, char *cret)
1559 {
1560         if (!cret) return -1;
1561
1562         return CtdlIPCGenericCommand(ipc, "DOWN", NULL, 0, NULL, NULL, cret);
1563 }
1564
1565
1566 /* SCDN */
1567 int CtdlIPCTerminateServerScheduled(CtdlIPC *ipc, int mode, char *cret)
1568 {
1569         char aaa[16];
1570
1571         if (!cret) return -1;
1572
1573         sprintf(aaa, "SCDN %d", mode ? 1 : 0);
1574         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1575 }
1576
1577
1578 /* EMSG */
1579 int CtdlIPCEnterSystemMessage(CtdlIPC *ipc, const char *filename, const char *text,
1580                 char *cret)
1581 {
1582         register int ret;
1583         char *aaa;
1584
1585         if (!cret) return -2;
1586         if (!text) return -2;
1587         if (!filename) return -2;
1588
1589         aaa = (char *)malloc(strlen(filename) + 6);
1590         if (!aaa) return -1;
1591
1592         sprintf(aaa, "EMSG %s", filename);
1593         ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1594         free(aaa);
1595         return ret;
1596 }
1597
1598
1599 /* HCHG */
1600 int CtdlIPCChangeHostname(CtdlIPC *ipc, const char *hostname, char *cret)
1601 {
1602         register int ret;
1603         char *aaa;
1604
1605         if (!cret) return -2;
1606         if (!hostname) return -2;
1607
1608         aaa = (char *)malloc(strlen(hostname) + 6);
1609         if (!aaa) return -1;
1610
1611         sprintf(aaa, "HCHG %s", hostname);
1612         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1613         free(aaa);
1614         return ret;
1615 }
1616
1617
1618 /* RCHG */
1619 int CtdlIPCChangeRoomname(CtdlIPC *ipc, const char *roomname, char *cret)
1620 {
1621         register int ret;
1622         char *aaa;
1623
1624         if (!cret) return -2;
1625         if (!roomname) return -2;
1626
1627         aaa = (char *)malloc(strlen(roomname) + 6);
1628         if (!aaa) return -1;
1629
1630         sprintf(aaa, "RCHG %s", roomname);
1631         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1632         free(aaa);
1633         return ret;
1634 }
1635
1636
1637 /* UCHG */
1638 int CtdlIPCChangeUsername(CtdlIPC *ipc, const char *username, char *cret)
1639 {
1640         register int ret;
1641         char *aaa;
1642
1643         if (!cret) return -2;
1644         if (!username) return -2;
1645
1646         aaa = (char *)malloc(strlen(username) + 6);
1647         if (!aaa) return -1;
1648
1649         sprintf(aaa, "UCHG %s", username);
1650         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1651         free(aaa);
1652         return ret;
1653 }
1654
1655
1656 /* TIME */
1657 /* This function returns the actual server time reported, or 0 if error */
1658 time_t CtdlIPCServerTime(CtdlIPC *ipc, char *cret)
1659 {
1660         register time_t tret;
1661         register int ret;
1662
1663         ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
1664         if (ret / 100 == 2) {
1665                 tret = extract_long(cret, 0);
1666         } else {
1667                 tret = 0L;
1668         }
1669         return tret;
1670 }
1671
1672
1673 /* AGUP */
1674 int CtdlIPCAideGetUserParameters(CtdlIPC *ipc, const char *who,
1675                                  struct usersupp **uret, char *cret)
1676 {
1677         register int ret;
1678         char aaa[SIZ];
1679
1680         if (!cret) return -2;
1681         if (!uret) return -2;
1682         if (!*uret) *uret = (struct usersupp *)calloc(1, sizeof(struct usersupp));
1683         if (!*uret) return -1;
1684
1685         sprintf(aaa, "AGUP %s", who);
1686         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1687
1688         if (ret / 100 == 2) {
1689                 extract(uret[0]->fullname, cret, 0);
1690                 extract(uret[0]->password, cret, 1);
1691                 uret[0]->flags = extract_int(cret, 2);
1692                 uret[0]->timescalled = extract_long(cret, 3);
1693                 uret[0]->posted = extract_long(cret, 4);
1694                 uret[0]->axlevel = extract_int(cret, 5);
1695                 uret[0]->usernum = extract_long(cret, 6);
1696                 uret[0]->lastcall = extract_long(cret, 7);
1697                 uret[0]->USuserpurge = extract_int(cret, 8);
1698         }
1699         return ret;
1700 }
1701
1702
1703 /* ASUP */
1704 int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct usersupp *uret, char *cret)
1705 {
1706         register int ret;
1707         char *aaa;
1708
1709         if (!cret) return -2;
1710         if (!uret) return -2;
1711
1712         aaa = (char *)malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
1713         if (!aaa) return -1;
1714
1715         sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
1716                         uret->fullname, uret->password, uret->flags,
1717                         uret->timescalled, uret->posted, uret->axlevel,
1718                         uret->usernum, uret->lastcall, uret->USuserpurge);
1719         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1720         free(aaa);
1721         return ret;
1722 }
1723
1724
1725 /* GPEX */
1726 /* which is 0 = room, 1 = floor, 2 = site */
1727 /* caller must free the struct ExpirePolicy */
1728 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1729                 struct ExpirePolicy **policy, char *cret)
1730 {
1731         static char *proto[] = {"room", "floor", "site"};
1732         char aaa[11];
1733         register int ret;
1734
1735         if (!cret) return -2;
1736         if (!policy) return -2;
1737         if (!*policy) *policy = (struct ExpirePolicy *)calloc(1, sizeof(struct ExpirePolicy));
1738         if (!*policy) return -1;
1739         if (which < 0 || which > 2) return -2;
1740         
1741         sprintf(aaa, "GPEX %s", proto[which]);
1742         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1743         if (ret / 100 == 2) {
1744                 policy[0]->expire_mode = extract_int(cret, 0);
1745                 policy[0]->expire_value = extract_int(cret, 1);
1746         }
1747         return ret;
1748
1749 }
1750
1751
1752 /* SPEX */
1753 /* which is 0 = room, 1 = floor, 2 = site */
1754 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
1755 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1756                 struct ExpirePolicy *policy, char *cret)
1757 {
1758         char aaa[38];
1759         char *whichvals[] = { "room", "floor", "site" };
1760
1761         if (!cret) return -2;
1762         if (which < 0 || which > 2) return -2;
1763         if (!policy) return -2;
1764         if (policy->expire_mode < 0 || policy->expire_mode > 3) return -2;
1765         if (policy->expire_mode >= 2 && policy->expire_value < 1) return -2;
1766
1767         sprintf(aaa, "SPEX %s|%d|%d", whichvals[which],
1768                         policy->expire_mode, policy->expire_value);
1769         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1770 }
1771
1772
1773 /* CONF GET */
1774 int CtdlIPCGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
1775 {
1776         size_t bytes;
1777
1778         if (!cret) return -2;
1779         if (!listing) return -2;
1780         if (*listing) return -2;
1781
1782         return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0,
1783                         listing, &bytes, cret);
1784 }
1785
1786
1787 /* CONF SET */
1788 int CtdlIPCSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
1789 {
1790         if (!cret) return -2;
1791         if (!listing) return -2;
1792
1793         return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing),
1794                         NULL, NULL, cret);
1795 }
1796
1797
1798 /* CONF GETSYS */
1799 int CtdlIPCGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1800                 char **listing, char *cret)
1801 {
1802         char *aaa;
1803         size_t bytes;
1804
1805         if (!cret) return -2;
1806         if (!mimetype) return -2;
1807         if (!listing) return -2;
1808         if (*listing) return -2;
1809
1810         aaa = malloc(strlen(mimetype) + 13);
1811         if (!aaa) return -1;
1812         sprintf(aaa, "CONF GETSYS|%s", mimetype);
1813         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0,
1814                         listing, &bytes, cret);
1815 }
1816
1817
1818 /* CONF PUTSYS */
1819 int CtdlIPCSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1820                const char *listing, char *cret)
1821 {
1822         char *aaa;
1823
1824         if (!cret) return -2;
1825         if (!mimetype) return -2;
1826         if (!listing) return -2;
1827
1828         aaa = malloc(strlen(mimetype) + 13);
1829         if (!aaa) return -1;
1830         sprintf(aaa, "CONF PUTSYS|%s", mimetype);
1831         return CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing),
1832                         NULL, NULL, cret);
1833 }
1834
1835
1836 /* REQT */
1837 int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
1838 {
1839         char aaa[16];
1840
1841         if (!cret) return -2;
1842         if (session < 0) return -2;
1843
1844         sprintf(aaa, "REQT %d", session);
1845         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1846 }
1847
1848
1849 /* SEEN */
1850 int CtdlIPCSetMessageSeen(CtdlIPC *ipc, long msgnum, int seen, char *cret)
1851 {
1852         char aaa[27];
1853
1854         if (!cret) return -2;
1855         if (msgnum < 0) return -2;
1856
1857         sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
1858         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1859 }
1860
1861
1862 /* STLS */
1863 int CtdlIPCStartEncryption(CtdlIPC *ipc, char *cret)
1864 {
1865         int a;
1866         int r;
1867         char buf[SIZ];
1868
1869 #ifdef HAVE_OPENSSL
1870         SSL *temp_ssl;
1871
1872         /* New SSL object */
1873         temp_ssl = SSL_new(ssl_ctx);
1874         if (!temp_ssl) {
1875                 error_printf("SSL_new failed: %s\n",
1876                                 ERR_reason_error_string(ERR_get_error()));
1877                 return -2;
1878         }
1879         /* Pointless flag waving */
1880 #if SSLEAY_VERSION_NUMBER >= 0x0922
1881         SSL_set_session_id_context(temp_ssl, "Citadel/UX SID", 14);
1882 #endif
1883
1884         if (!access("/var/run/egd-pool", F_OK))
1885                 RAND_egd("/var/run/egd-pool");
1886
1887         if (!RAND_status()) {
1888                 error_printf("PRNG not properly seeded\n");
1889                 return -2;
1890         }
1891
1892         /* Associate network connection with SSL object */
1893         if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
1894                 error_printf("SSL_set_fd failed: %s\n",
1895                                 ERR_reason_error_string(ERR_get_error()));
1896                 return -2;
1897         }
1898
1899         if (status_hook != NULL)
1900                 status_hook("Requesting encryption...\r");
1901
1902         /* Ready to start SSL/TLS */
1903         /* Old code
1904         CtdlIPC_putline(ipc, "STLS");
1905         CtdlIPC_getline(ipc, buf);
1906         if (buf[0] != '2') {
1907                 error_printf("Server can't start TLS: %s\n", buf);
1908                 return 0;
1909         }
1910         */
1911         r = CtdlIPCGenericCommand(ipc,
1912                                   "STLS", NULL, 0, NULL, NULL, cret);
1913         if (r / 100 != 2) {
1914                 error_printf("Server can't start TLS: %s\n", buf);
1915                 endtls(temp_ssl);
1916                 return r;
1917         }
1918
1919         /* Do SSL/TLS handshake */
1920         if ((a = SSL_connect(temp_ssl)) < 1) {
1921                 error_printf("SSL_connect failed: %s\n",
1922                                 ERR_reason_error_string(ERR_get_error()));
1923                 endtls(temp_ssl);
1924                 return -2;
1925         }
1926         ipc->ssl = temp_ssl;
1927
1928         BIO_set_close(ipc->ssl->rbio, BIO_NOCLOSE);
1929         {
1930                 int bits, alg_bits;
1931
1932                 bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(ipc->ssl), &alg_bits);
1933                 error_printf("Encrypting with %s cipher %s (%d of %d bits)\n",
1934                                 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
1935                                 SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl)),
1936                                 bits, alg_bits);
1937         }
1938         return r;
1939 #else
1940         return 0;
1941 #endif /* HAVE_OPENSSL */
1942 }
1943
1944
1945 #ifdef HAVE_OPENSSL
1946 static void endtls(SSL *ssl)
1947 {
1948         if (ssl) {
1949                 SSL_shutdown(ssl);
1950                 SSL_free(ssl);
1951         }
1952 }
1953 #endif
1954
1955
1956 /* QDIR */
1957 int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret)
1958 {
1959         char *aaa;
1960
1961         if (!address) return -2;
1962         if (!cret) return -2;
1963
1964         aaa = (char *)malloc(strlen(address) + 6);
1965         if (!aaa) return -1;
1966
1967         sprintf(aaa, "QDIR %s", address);
1968         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1969 }
1970
1971
1972 /* IPGM */
1973 int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
1974 {
1975         char aaa[30];
1976
1977         if (!cret) return -2;
1978         sprintf(aaa, "IPGM %d", secret);
1979         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1980 }
1981
1982
1983 /* FSCK */
1984 int CtdlIPCMessageBaseCheck(CtdlIPC *ipc, char **mret, char *cret)
1985 {
1986         size_t size = 0;
1987
1988         if (!cret) return -2;
1989         if (!mret) return -2;
1990         if (*mret) return -2;
1991
1992         return CtdlIPCGenericCommand(ipc, "FSCK", NULL, 0, mret, &size, cret);
1993 }
1994
1995
1996 /*
1997  * Not implemented:
1998  * 
1999  * CHAT
2000  * ETLS
2001  * EXPI
2002  * GTLS
2003  * IGAB
2004  * MSG3
2005  * MSG4
2006  * NDOP
2007  * NETP
2008  * NUOP
2009  * SMTP
2010  */
2011
2012
2013 /* ************************************************************************** */
2014 /*             Stuff below this line is not for public consumption            */
2015 /* ************************************************************************** */
2016
2017
2018 inline void CtdlIPC_lock(CtdlIPC *ipc)
2019 {
2020         if (ipc->network_status_cb) ipc->network_status_cb(1);
2021 #ifdef THREADED_CLIENT
2022         pthread_mutex_lock(&(ipc->mutex));
2023 #endif
2024 }
2025
2026
2027 inline void CtdlIPC_unlock(CtdlIPC *ipc)
2028 {
2029 #ifdef THREADED_CLIENT
2030         pthread_mutex_unlock(&(ipc->mutex));
2031 #endif
2032         if (ipc->network_status_cb) ipc->network_status_cb(0);
2033 }
2034
2035
2036 /* Read a listing from the server up to 000.  Append to dest if it exists */
2037 char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest)
2038 {
2039         size_t length = 0;
2040         size_t linelength;
2041         char *ret = NULL;
2042         char aaa[SIZ];
2043
2044         ret = dest;
2045         if (ret != NULL) {
2046                 length = strlen(ret);
2047         } else {
2048                 length = 0;
2049         }
2050
2051         while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2052                 linelength = strlen(aaa);
2053                 ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
2054                 if (ret) {
2055                         strcpy(&ret[length], aaa);
2056                         length += linelength;
2057                         strcpy(&ret[length++], "\n");
2058                 }
2059         }
2060
2061         return(ret);
2062 }
2063
2064
2065 /* Send a listing to the server; generate the ending 000. */
2066 int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing)
2067 {
2068         char *text;
2069
2070         text = (char *)malloc(strlen(listing) + 6);
2071         if (text) {
2072                 strcpy(text, listing);
2073                 while (text[strlen(text) - 1] == '\n')
2074                         text[strlen(text) - 1] = '\0';
2075                 strcat(text, "\n000");
2076                 CtdlIPC_putline(ipc, text);
2077                 free(text);
2078                 text = NULL;
2079         } else {
2080                 /* Malloc failed but we are committed to send */
2081                 /* This may result in extra blanks at the bottom */
2082                 CtdlIPC_putline(ipc, text);
2083                 CtdlIPC_putline(ipc, "000");
2084         }
2085         return 0;
2086 }
2087
2088
2089 /* Partial read of file from server */
2090 size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes, char *cret)
2091 {
2092         register size_t len = 0;
2093         char aaa[SIZ];
2094
2095         if (!buf) return 0;
2096         if (!cret) return 0;
2097         if (bytes < 1) return 0;
2098         if (offset < 0) return 0;
2099
2100         CtdlIPC_lock(ipc);
2101         sprintf(aaa, "READ %d|%d", (int)offset, (int)bytes);
2102         CtdlIPC_putline(ipc, aaa);
2103         CtdlIPC_getline(ipc, aaa);
2104         if (aaa[0] != '6')
2105                 strcpy(cret, &aaa[4]);
2106         else {
2107                 len = extract_long(&aaa[4], 0);
2108                 *buf = (void *)realloc(*buf, (size_t)(offset + len));
2109                 if (*buf) {
2110                         /* I know what I'm doing */
2111                         serv_read(ipc, (*buf + offset), len);
2112                 } else {
2113                         /* We have to read regardless */
2114                         serv_read(ipc, aaa, len);
2115                         len = 0;
2116                 }
2117         }
2118         CtdlIPC_unlock(ipc);
2119         return len;
2120 }
2121
2122
2123 /* CLOS */
2124 int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
2125 {
2126         register int ret;
2127
2128         if (!cret) return -2;
2129         if (!ipc->downloading) return -2;
2130
2131         ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2132         if (ret / 100 == 2)
2133                 ipc->downloading = 0;
2134         return ret;
2135 }
2136
2137
2138 /* MSGP */
2139 int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
2140         register int ret;
2141         char cmd[SIZ];
2142         
2143         snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2144         ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2145         return ret;
2146 }
2147
2148
2149
2150 /* READ */
2151 int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
2152                void (*progress_gauge_callback)(unsigned long, unsigned long),
2153                char *cret)
2154 {
2155         register size_t len;
2156
2157         if (!cret) return -1;
2158         if (!buf) return -1;
2159         if (*buf) return -1;
2160         if (!ipc->downloading) return -1;
2161
2162         len = resume;
2163         if (progress_gauge_callback)
2164                 progress_gauge_callback(len, bytes);
2165         while (len < bytes) {
2166                 register size_t block;
2167
2168                 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2169                 if (block == 0) {
2170                         free(*buf);
2171                         return 0;
2172                 }
2173                 len += block;
2174                 if (progress_gauge_callback)
2175                         progress_gauge_callback(len, bytes);
2176         }
2177         return len;
2178 }
2179
2180
2181 /* READ - pipelined */
2182 int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
2183                size_t resume,
2184                void (*progress_gauge_callback)(unsigned long, unsigned long),
2185                char *cret)
2186 {
2187         register size_t len;
2188         register int calls;     /* How many calls in the pipeline */
2189         register int i;         /* iterator */
2190         char aaa[4096];
2191
2192         if (!cret) return -1;
2193         if (!buf) return -1;
2194         if (*buf) return -1;
2195         if (!ipc->downloading) return -1;
2196
2197         *buf = (void *)realloc(*buf, bytes - resume);
2198         if (!*buf) return -1;
2199
2200         len = 0;
2201         CtdlIPC_lock(ipc);
2202         if (progress_gauge_callback)
2203                 progress_gauge_callback(len, bytes);
2204
2205         /* How many calls will be in the pipeline? */
2206         calls = (bytes - resume) / 4096;
2207         if ((bytes - resume) % 4096) calls++;
2208
2209         /* Send all requests at once */
2210         for (i = 0; i < calls; i++) {
2211                 sprintf(aaa, "READ %d|4096", (int)(i * 4096 + resume) );
2212                 CtdlIPC_putline(ipc, aaa);
2213         }
2214
2215         /* Receive all responses at once */
2216         for (i = 0; i < calls; i++) {
2217                 CtdlIPC_getline(ipc, aaa);
2218                 if (aaa[0] != '6')
2219                         strcpy(cret, &aaa[4]);
2220                 else {
2221                         len = extract_long(&aaa[4], 0);
2222                         /* I know what I'm doing */
2223                         serv_read(ipc, ((*buf) + (i * 4096)), len);
2224                 }
2225                 if (progress_gauge_callback)
2226                         progress_gauge_callback(i * 4096 + len, bytes);
2227         }
2228         CtdlIPC_unlock(ipc);
2229         return len;
2230 }
2231
2232
2233 /* UCLS */
2234 int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
2235 {
2236         register int ret;
2237         char cmd[8];
2238
2239         if (!cret) return -1;
2240         if (!ipc->uploading) return -1;
2241
2242         sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2243         ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2244         ipc->uploading = 0;
2245         return ret;
2246 }
2247
2248
2249 /* WRIT */
2250 int CtdlIPCWriteUpload(CtdlIPC *ipc, const char *path,
2251                 void (*progress_gauge_callback)(unsigned long, unsigned long),
2252                 char *cret)
2253 {
2254         register int ret = -1;
2255         register size_t offset = 0;
2256         size_t bytes;
2257         char aaa[SIZ];
2258         char buf[4096];
2259         FILE *fd;
2260
2261         if (!cret) return -1;
2262         if (!path) return -1;
2263         if (!*path) return -1;
2264
2265         fd = fopen(path, "r");
2266         if (!fd) return -2;
2267
2268         fseek(fd, 0L, SEEK_END);
2269         bytes = ftell(fd);
2270         rewind(fd);
2271
2272         if (progress_gauge_callback)
2273                 progress_gauge_callback(0, bytes);
2274
2275         while (offset < bytes) {
2276                 register size_t to_write;
2277
2278                 /* Read some data in */
2279                 to_write = fread(buf, 1, 4096, fd);
2280                 if (!to_write) {
2281                         if (feof(fd) || ferror(fd)) break;
2282                 }
2283                 sprintf(aaa, "WRIT %d", (int)to_write);
2284                 CtdlIPC_putline(ipc, aaa);
2285                 CtdlIPC_getline(ipc, aaa);
2286                 strcpy(cret, &aaa[4]);
2287                 ret = atoi(aaa);
2288                 if (aaa[0] == '7') {
2289                         to_write = extract_long(&aaa[4], 0);
2290                         
2291                         serv_write(ipc, buf, to_write);
2292                         offset += to_write;
2293                         if (progress_gauge_callback)
2294                                 progress_gauge_callback(offset, bytes);
2295                         /* Detect short reads and back up if needed */
2296                         /* offset will never be negative anyway */
2297                         fseek(fd, (signed)offset, SEEK_SET);
2298                 } else {
2299                         break;
2300                 }
2301         }
2302         if (progress_gauge_callback)
2303                 progress_gauge_callback(1, 1);
2304         return (!ferror(fd) ? ret : -2);
2305 }
2306
2307
2308 /*
2309  * Generic command method.  This method should handle any server command
2310  * except for CHAT.  It takes the following arguments:
2311  *
2312  * ipc                  The server to speak with
2313  * command              Preformatted command to send to server
2314  * to_send              A text or binary file to send to server
2315  *                      (only sent if server requests it)
2316  * bytes_to_send        The number of bytes in to_send (required if
2317  *                      sending binary, optional if sending listing)
2318  * to_receive           Pointer to a NULL pointer, if the server
2319  *                      sends text or binary we will allocate memory
2320  *                      for the file and stuff it here
2321  * bytes_to_receive     If a file is received, we will store its
2322  *                      byte count here
2323  * proto_response       The protocol response.  Caller must provide
2324  *                      this buffer and ensure that it is at least
2325  *                      128 bytes in length.
2326  *
2327  * This function returns a number equal to the protocol response number,
2328  * -1 if an internal error occurred, -2 if caller provided bad values,
2329  * or 0 - the protocol response number if bad values were found during
2330  * the protocol exchange.
2331  * It stores the protocol response string (minus the number) in 
2332  * protocol_response as described above.  Some commands send additional
2333  * data in this string.
2334  */
2335 int CtdlIPCGenericCommand(CtdlIPC *ipc,
2336                 const char *command, const char *to_send,
2337                 size_t bytes_to_send, char **to_receive, 
2338                 size_t *bytes_to_receive, char *proto_response)
2339 {
2340         char buf[SIZ];
2341         register int ret;
2342         int watch_ssl = 0;
2343
2344         if (!command) return -2;
2345         if (!proto_response) return -2;
2346
2347 #ifdef HAVE_OPENSSL
2348         if (ipc->ssl) watch_ssl = 1;
2349 #endif
2350
2351         CtdlIPC_lock(ipc);
2352         CtdlIPC_putline(ipc, command);
2353         while (1) {
2354                 CtdlIPC_getline(ipc, proto_response);
2355                 if (proto_response[3] == '*')
2356                         express_msgs = 1;
2357                 ret = atoi(proto_response);
2358                 strcpy(proto_response, &proto_response[4]);
2359                 switch (ret / 100) {
2360                 default:                        /* Unknown, punt */
2361                 case 2:                         /* OK */
2362                 case 3:                         /* MORE_DATA */
2363                 case 5:                         /* ERROR */
2364                         /* Don't need to do anything */
2365                         break;
2366                 case 1:                         /* LISTING_FOLLOWS */
2367                         if (to_receive && !*to_receive && bytes_to_receive) {
2368                                 *to_receive = CtdlIPCReadListing(ipc, NULL);
2369                         } else { /* Drain */
2370                                 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) ;
2371                                 ret = -ret;
2372                         }
2373                         break;
2374                 case 4:                         /* SEND_LISTING */
2375                         if (to_send) {
2376                                 CtdlIPCSendListing(ipc, to_send);
2377                         } else {
2378                                 /* No listing given, fake it */
2379                                 CtdlIPC_putline(ipc, "000");
2380                                 ret = -ret;
2381                         }
2382                         break;
2383                 case 6:                         /* BINARY_FOLLOWS */
2384                         if (to_receive && !*to_receive && bytes_to_receive) {
2385                                 *bytes_to_receive =
2386                                         extract_long(proto_response, 0);
2387                                 *to_receive = (char *)
2388                                         malloc((size_t)*bytes_to_receive);
2389                                 if (!*to_receive) {
2390                                         ret = -1;
2391                                 } else {
2392                                         serv_read(ipc, *to_receive,
2393                                                         *bytes_to_receive);
2394                                 }
2395                         } else {
2396                                 /* Drain */
2397                                 size_t drain;
2398
2399                                 drain = extract_long(proto_response, 0);
2400                                 while (drain > SIZ) {
2401                                         serv_read(ipc, buf, SIZ);
2402                                         drain -= SIZ;
2403                                 }
2404                                 serv_read(ipc, buf, drain);
2405                                 ret = -ret;
2406                         }
2407                         break;
2408                 case 7:                         /* SEND_BINARY */
2409                         if (to_send && bytes_to_send) {
2410                                 serv_write(ipc, to_send, bytes_to_send);
2411                         } else if (bytes_to_send) {
2412                                 /* Fake it, send nulls */
2413                                 size_t fake;
2414
2415                                 fake = bytes_to_send;
2416                                 memset(buf, '\0', SIZ);
2417                                 while (fake > SIZ) {
2418                                         serv_write(ipc, buf, SIZ);
2419                                         fake -= SIZ;
2420                                 }
2421                                 serv_write(ipc, buf, fake);
2422                                 ret = -ret;
2423                         } /* else who knows?  DANGER WILL ROBINSON */
2424                         break;
2425                 case 8:                         /* START_CHAT_MODE */
2426                         if (!strncasecmp(command, "CHAT", 4)) {
2427                                 /* Don't call chatmode with generic! */
2428                                 CtdlIPC_putline(ipc, "/quit");
2429                                 ret = -ret;
2430                         } else {
2431                                 /* In this mode we send then receive listing */
2432                                 if (to_send) {
2433                                         CtdlIPCSendListing(ipc, to_send);
2434                                 } else {
2435                                         /* No listing given, fake it */
2436                                         CtdlIPC_putline(ipc, "000");
2437                                         ret = -ret;
2438                                 }
2439                                 if (to_receive && !*to_receive
2440                                                 && bytes_to_receive) {
2441                                         *to_receive = CtdlIPCReadListing(ipc, NULL);
2442                                 } else { /* Drain */
2443                                         while (CtdlIPC_getline(ipc, buf),
2444                                                         strcmp(buf, "000")) ;
2445                                         ret = -ret;
2446                                 }
2447                         }
2448                         break;
2449                 case 9:                         /* ASYNC_MSG */
2450                         /* CtdlIPCDoAsync(ret, proto_response); */
2451                         free(CtdlIPCReadListing(ipc, NULL));    /* STUB FIXME */
2452                         break;
2453                 }
2454                 if (ret / 100 != 9)
2455                         break;
2456         }
2457         CtdlIPC_unlock(ipc);
2458         return ret;
2459 }
2460
2461
2462 static int connectsock(char *host, char *service, char *protocol, int defaultPort)
2463 {
2464         struct hostent *phe;
2465         struct servent *pse;
2466         struct protoent *ppe;
2467         struct sockaddr_in sin;
2468         int s, type;
2469
2470         memset(&sin, 0, sizeof(sin));
2471         sin.sin_family = AF_INET;
2472
2473         pse = getservbyname(service, protocol);
2474         if (pse != NULL) {
2475                 sin.sin_port = pse->s_port;
2476         }
2477         else if (atoi(service) > 0) {
2478                 sin.sin_port = htons(atoi(service));
2479         }
2480         else {
2481                 sin.sin_port = htons(defaultPort);
2482         }
2483         phe = gethostbyname(host);
2484         if (phe) {
2485                 memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
2486         } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
2487                 return -1;
2488         }
2489         if ((ppe = getprotobyname(protocol)) == 0) {
2490                 return -1;
2491         }
2492         if (!strcmp(protocol, "udp")) {
2493                 type = SOCK_DGRAM;
2494         } else {
2495                 type = SOCK_STREAM;
2496         }
2497
2498         s = socket(PF_INET, type, ppe->p_proto);
2499         if (s < 0) {
2500                 return -1;
2501         }
2502
2503         if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
2504                 return -1;
2505         }
2506
2507         return (s);
2508 }
2509
2510 static int uds_connectsock(int *isLocal, char *sockpath)
2511 {
2512         struct sockaddr_un addr;
2513         int s;
2514
2515         memset(&addr, 0, sizeof(addr));
2516         addr.sun_family = AF_UNIX;
2517         safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2518
2519         s = socket(AF_UNIX, SOCK_STREAM, 0);
2520         if (s < 0) {
2521                 return -1;
2522         }
2523
2524         if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2525                 return -1;
2526         }
2527
2528         *isLocal = 1;
2529         return s;
2530 }
2531
2532
2533 /*
2534  * input binary data from socket
2535  */
2536 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes)
2537 {
2538         unsigned int len, rlen;
2539
2540 #if defined(HAVE_OPENSSL)
2541         if (ipc->ssl) {
2542                 serv_read_ssl(ipc, buf, bytes);
2543                 return;
2544         }
2545 #endif
2546         len = 0;
2547         while (len < bytes) {
2548                 rlen = read(ipc->sock, &buf[len], bytes - len);
2549                 if (rlen < 1) {
2550                         connection_died(ipc);
2551                         return;
2552                 }
2553                 len += rlen;
2554         }
2555 }
2556
2557
2558 /*
2559  * send binary to server
2560  */
2561 static void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2562 {
2563         unsigned int bytes_written = 0;
2564         int retval;
2565
2566 #if defined(HAVE_OPENSSL)
2567         if (ipc->ssl) {
2568                 serv_write_ssl(ipc, buf, nbytes);
2569                 return;
2570         }
2571 #endif
2572         while (bytes_written < nbytes) {
2573                 retval = write(ipc->sock, &buf[bytes_written],
2574                                nbytes - bytes_written);
2575                 if (retval < 1) {
2576                         connection_died(ipc);
2577                         return;
2578                 }
2579                 bytes_written += retval;
2580         }
2581 }
2582
2583
2584 #ifdef HAVE_OPENSSL
2585 /*
2586  * input binary data from encrypted connection
2587  */
2588 static void serv_read_ssl(CtdlIPC* ipc, char *buf, unsigned int bytes)
2589 {
2590         int len, rlen;
2591         char junk[1];
2592
2593         len = 0;
2594         while (len < bytes) {
2595                 if (SSL_want_read(ipc->ssl)) {
2596                         if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2597                                 error_printf("SSL_write in serv_read:\n");
2598                                 ERR_print_errors_fp(stderr);
2599                         }
2600                 }
2601                 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2602                 if (rlen < 1) {
2603                         long errval;
2604
2605                         errval = SSL_get_error(ipc->ssl, rlen);
2606                         if (errval == SSL_ERROR_WANT_READ ||
2607                                         errval == SSL_ERROR_WANT_WRITE) {
2608                                 sleep(1);
2609                                 continue;
2610                         }
2611                         if (errval == SSL_ERROR_ZERO_RETURN ||
2612                                         errval == SSL_ERROR_SSL) {
2613                                 serv_read(ipc, &buf[len], bytes - len);
2614                                 return;
2615                         }
2616                         error_printf("SSL_read in serv_read:\n");
2617                         ERR_print_errors_fp(stderr);
2618                         connection_died(NULL);
2619                         return;
2620                 }
2621                 len += rlen;
2622         }
2623 }
2624
2625
2626 /*
2627  * send binary to server encrypted
2628  */
2629 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2630 {
2631         unsigned int bytes_written = 0;
2632         int retval;
2633         char junk[1];
2634
2635         while (bytes_written < nbytes) {
2636                 if (SSL_want_write(ipc->ssl)) {
2637                         if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
2638                                 error_printf("SSL_read in serv_write:\n");
2639                                 ERR_print_errors_fp(stderr);
2640                         }
2641                 }
2642                 retval = SSL_write(ipc->ssl, &buf[bytes_written],
2643                                 nbytes - bytes_written);
2644                 if (retval < 1) {
2645                         long errval;
2646
2647                         errval = SSL_get_error(ipc->ssl, retval);
2648                         if (errval == SSL_ERROR_WANT_READ ||
2649                                         errval == SSL_ERROR_WANT_WRITE) {
2650                                 sleep(1);
2651                                 continue;
2652                         }
2653                         if (errval == SSL_ERROR_ZERO_RETURN ||
2654                                         errval == SSL_ERROR_SSL) {
2655                                 serv_write(ipc, &buf[bytes_written],
2656                                                 nbytes - bytes_written);
2657                                 return;
2658                         }
2659                         error_printf("SSL_write in serv_write:\n");
2660                         ERR_print_errors_fp(stderr);
2661                         connection_died(NULL);
2662                         return;
2663                 }
2664                 bytes_written += retval;
2665         }
2666 }
2667
2668
2669 static void CtdlIPC_init_OpenSSL(void)
2670 {
2671         int a;
2672         SSL_METHOD *ssl_method;
2673         DH *dh;
2674         
2675         /* already done init */
2676         if (ssl_ctx) {
2677                 return;
2678         }
2679
2680         /* Get started */
2681         ssl_ctx = NULL;
2682         dh = NULL;
2683         SSL_load_error_strings();
2684         SSLeay_add_ssl_algorithms();
2685
2686         /* Set up the SSL context in which we will oeprate */
2687         ssl_method = SSLv23_client_method();
2688         ssl_ctx = SSL_CTX_new(ssl_method);
2689         if (!ssl_ctx) {
2690                 error_printf("SSL_CTX_new failed: %s\n",
2691                                 ERR_reason_error_string(ERR_get_error()));
2692                 return;
2693         }
2694         /* Any reasonable cipher we can get */
2695         if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
2696                 error_printf("No ciphers available for encryption\n");
2697                 return;
2698         }
2699         SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
2700         
2701         /* Load DH parameters into the context */
2702         dh = DH_new();
2703         if (!dh) {
2704                 error_printf("Can't allocate a DH object: %s\n",
2705                                 ERR_reason_error_string(ERR_get_error()));
2706                 return;
2707         }
2708         if (!(BN_hex2bn(&(dh->p), DH_P))) {
2709                 error_printf("Can't assign DH_P: %s\n",
2710                                 ERR_reason_error_string(ERR_get_error()));
2711                 DH_free(dh);
2712                 return;
2713         }
2714         if (!(BN_hex2bn(&(dh->g), DH_G))) {
2715                 error_printf("Can't assign DH_G: %s\n",
2716                                 ERR_reason_error_string(ERR_get_error()));
2717                 DH_free(dh);
2718                 return;
2719         }
2720         dh->length = DH_L;
2721         SSL_CTX_set_tmp_dh(ssl_ctx, dh);
2722         DH_free(dh);
2723
2724 #ifdef THREADED_CLIENT
2725         /* OpenSSL requires callbacks for threaded clients */
2726         CRYPTO_set_locking_callback(ssl_lock);
2727         CRYPTO_set_id_callback(id_callback);
2728
2729         /* OpenSSL requires us to do semaphores for threaded clients */
2730         Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
2731         if (!Critters) {
2732                 perror("malloc failed");
2733                 exit(1);
2734         } else {
2735                 for (a = 0; a < CRYPTO_num_locks(); a++) {
2736                         Critters[a] = malloc(sizeof (pthread_mutex_t));
2737                         if (!Critters[a]) {
2738                                 perror("malloc failed");
2739                                 exit(1);
2740                         }
2741                         pthread_mutex_init(Critters[a], NULL);
2742                 }
2743         }
2744 #endif /* THREADED_CLIENT */       
2745 }
2746
2747
2748 static void ssl_lock(int mode, int n, const char *file, int line)
2749 {
2750 #ifdef THREADED_CLIENT
2751         if (mode & CRYPTO_LOCK)
2752                 pthread_mutex_lock(Critters[n]);
2753         else
2754                 pthread_mutex_unlock(Critters[n]);
2755 #endif /* THREADED_CLIENT */
2756 }
2757
2758 #ifdef THREADED_CLIENT
2759 static unsigned long id_callback(void) {
2760         return (unsigned long)pthread_self();
2761 }
2762 #endif /* THREADED_CLIENT */
2763 #endif /* HAVE_OPENSSL */
2764
2765
2766 /*
2767  * input string from socket - implemented in terms of serv_read()
2768  */
2769 void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
2770 {
2771         int i;
2772
2773         /* Read one character at a time. */
2774         for (i = 0;; i++) {
2775                 serv_read(ipc, &buf[i], 1);
2776                 if (buf[i] == '\n' || i == (SIZ-1))
2777                         break;
2778         }
2779
2780         /* If we got a long line, discard characters until the newline. */
2781         if (i == (SIZ-1))
2782                 while (buf[i] != '\n')
2783                         serv_read(ipc, &buf[i], 1);
2784
2785         /* Strip the trailing newline.
2786          */
2787         buf[i] = 0;
2788 }
2789
2790
2791 /*
2792  * send line to server - implemented in terms of serv_write()
2793  */
2794 void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
2795 {
2796         /* error_printf("< %s\n", buf); */
2797         serv_write(ipc, buf, strlen(buf));
2798         serv_write(ipc, "\n", 1);
2799
2800         ipc->last_command_sent = time(NULL);
2801 }
2802
2803
2804 /*
2805  * attach to server
2806  */
2807 CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
2808 {
2809         int a;
2810         char cithost[SIZ];
2811         char citport[SIZ];
2812         char sockpath[SIZ];
2813
2814         CtdlIPC *ipc = ialloc(CtdlIPC);
2815         if (!ipc) {
2816                 return 0;
2817         }
2818 #if defined(HAVE_OPENSSL)
2819         ipc->ssl = NULL;
2820         CtdlIPC_init_OpenSSL();
2821 #endif
2822 #if defined(HAVE_PTHREAD_H)
2823         pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
2824 #endif
2825         ipc->sock = -1;                 /* Not connected */
2826         ipc->isLocal = 0;               /* Not local, of course! */
2827         ipc->downloading = 0;
2828         ipc->uploading = 0;
2829         ipc->last_command_sent = 0L;
2830         ipc->network_status_cb = NULL;
2831
2832         strcpy(cithost, DEFAULT_HOST);  /* default host */
2833         strcpy(citport, DEFAULT_PORT);  /* default port */
2834
2835         for (a = 0; a < argc; ++a) {
2836                 if (a == 0) {
2837                         /* do nothing */
2838                 } else if (a == 1) {
2839                         strcpy(cithost, argv[a]);
2840                 } else if (a == 2) {
2841                         strcpy(citport, argv[a]);
2842                 } else {
2843                         error_printf("%s: usage: ",argv[0]);
2844                         error_printf("%s [host] [port] ",argv[0]);
2845                         ifree(ipc);
2846                         errno = EINVAL;
2847                         return 0;
2848                 }
2849         }
2850
2851         if ((!strcmp(cithost, "localhost"))
2852            || (!strcmp(cithost, "127.0.0.1"))) {
2853                 ipc->isLocal = 1;
2854         }
2855
2856         /* If we're using a unix domain socket we can do a bunch of stuff */
2857         if (!strcmp(cithost, UDS)) {
2858                 snprintf(sockpath, sizeof sockpath, BBSDIR "/citadel.socket");
2859                 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
2860                 if (ipc->sock == -1) {
2861                         ifree(ipc);
2862                         return 0;
2863                 }
2864                 if (hostbuf != NULL) strcpy(hostbuf, cithost);
2865                 if (portbuf != NULL) strcpy(portbuf, sockpath);
2866                 return ipc;
2867         }
2868
2869         ipc->sock = connectsock(cithost, citport, "tcp", 504);
2870         if (ipc->sock == -1) {
2871                 ifree(ipc);
2872                 return 0;
2873         }
2874         if (hostbuf != NULL) strcpy(hostbuf, cithost);
2875         if (portbuf != NULL) strcpy(portbuf, citport);
2876         return ipc;
2877 }
2878
2879 /*
2880  * return the file descriptor of the server socket so we can select() on it.
2881  *
2882  * FIXME: This is only used in chat mode; eliminate it when chat mode gets
2883  * rewritten...
2884  */
2885 int CtdlIPC_getsockfd(CtdlIPC* ipc)
2886 {
2887         return ipc->sock;
2888 }
2889
2890
2891 /*
2892  * return one character
2893  *
2894  * FIXME: This is only used in chat mode; eliminate it when chat mode gets
2895  * rewritten...
2896  */
2897 char CtdlIPC_get(CtdlIPC* ipc)
2898 {
2899         char buf[2];
2900         char ch;
2901
2902         serv_read(ipc, buf, 1);
2903         ch = (int) buf[0];
2904
2905         return (ch);
2906 }