1 /**
2  * Copyright (c) 2001-2002 artofcode LLC.
3  *
4  * Permission is hereby granted, free of charge, to any person
5  * obtaining a copy of this software and associated documentation
6  * files (the "Software"), to deal in the Software without
7  * restriction, including without limitation the rights to use, copy,
8  * modify, merge, publish, distribute, sublicense, and/or sell copies
9  * of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23 **/
24 
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include "unistd_.h"
28 #include <string.h>
29 
30 #include "ijs.h"
31 #include "ijs_server.h"
32 
33 #define noVERBOSE
34 
35 typedef enum {
36   IJS_N_CHAN_SET = 1,
37   IJS_BPS_SET = 2,
38   IJS_CS_SET = 4,
39   IJS_WIDTH_SET = 8,
40   IJS_HEIGHT_SET = 16,
41   IJS_DPI_SET = 32
42 } IjsFieldsSet;
43 
44 #define IJS_FIELDS_REQUIRED (IJS_N_CHAN_SET | IJS_BPS_SET | IJS_CS_SET | \
45   IJS_WIDTH_SET | IJS_HEIGHT_SET | IJS_DPI_SET)
46 
47 struct _IjsServerCtx {
48   int fd_from;
49   int child_pid;
50   IjsSendChan send_chan;
51   IjsRecvChan recv_chan;
52   int version;
53 
54   /* callbacks */
55   IjsBeginJobCb *begin_cb;
56   void *begin_cb_data;
57   IjsEndJobCb *end_cb;
58   void *end_cb_data;
59   IjsQueryStatusCb *status_cb;
60   void *status_cb_data;
61   IjsListParamsCb *list_cb;
62   void *list_cb_data;
63   IjsEnumParamCb *enum_cb;
64   void *enum_cb_data;
65   IjsSetParamCb *set_cb;
66   void *set_cb_data;
67   IjsGetParamCb *get_cb;
68   void *get_cb_data;
69 
70   ijs_bool in_job;
71   IjsJobId job_id;
72 
73   IjsPageHeader *ph;
74 
75   /* This should be IjsFieldsSet, but David Suffield reports that this
76      causes problems when compiling with g++. */
77   int fields_set;
78   ijs_bool in_page;
79 
80   char *buf;
81   int buf_size;
82   int buf_ix;
83   char *overflow_buf;
84   int overflow_buf_size;
85   int overflow_buf_ix;
86 };
87 
88 static int
ijs_server_dummy_begin_cb(void * begin_cb_data,IjsServerCtx * ctx,IjsJobId job_id)89 ijs_server_dummy_begin_cb (void *begin_cb_data,
90 			   IjsServerCtx *ctx,
91 			   IjsJobId job_id)
92 {
93   return 0;
94 }
95 
96 static int
ijs_server_dummy_end_cb(void * end_cb_data,IjsServerCtx * ctx,IjsJobId job_id)97 ijs_server_dummy_end_cb (void *end_cb_data,
98 			 IjsServerCtx *ctx,
99 			 IjsJobId job_id)
100 {
101   return 0;
102 }
103 
104 IjsServerCtx *
ijs_server_init(void)105 ijs_server_init (void)
106 {
107   ijs_bool ok = TRUE;
108   char helo_buf[8];
109   char resp_buf[8];
110   int nbytes;
111   IjsServerCtx *ctx = (IjsServerCtx *)malloc (sizeof(IjsServerCtx));
112   int fd_from, fd_to;
113 
114   memcpy (resp_buf, IJS_RESP_STR, sizeof(resp_buf));
115 
116   fd_from = 0;
117   fd_to = 1;
118 #ifdef _MSC_VER
119   _setmode(fd_from, _O_BINARY);
120   _setmode(fd_to, _O_BINARY);
121 #endif
122 #ifdef VERBOSE
123   fprintf (stderr, "fd_from = %d, fd_to = %d\n", fd_from, fd_to);
124 #endif
125   ijs_recv_init (&ctx->recv_chan, fd_from);
126   ijs_send_init (&ctx->send_chan, fd_to);
127   nbytes = read (ctx->recv_chan.fd, helo_buf, sizeof(helo_buf));
128 
129   if (nbytes != sizeof(helo_buf))
130     ok = FALSE;
131 
132   if (ok)
133     nbytes = write (ctx->send_chan.fd, resp_buf, sizeof(resp_buf));
134   if (nbytes != sizeof(resp_buf))
135     ok = FALSE;
136 
137   ctx->in_job = FALSE;
138   ctx->job_id = -1;
139 
140   ctx->ph = NULL;
141   ctx->in_page = FALSE;
142   ctx->buf = NULL;
143   ctx->overflow_buf = NULL;
144 
145   ctx->begin_cb = ijs_server_dummy_begin_cb;
146   ctx->end_cb = ijs_server_dummy_end_cb;
147 
148   if (ok)
149     return ctx;
150   else
151     {
152       ijs_server_done (ctx);
153       return NULL;
154     }
155 }
156 
157 int
ijs_server_install_begin_cb(IjsServerCtx * ctx,IjsBeginJobCb * begin_cb,void * begin_cb_data)158 ijs_server_install_begin_cb (IjsServerCtx *ctx,
159 			     IjsBeginJobCb *begin_cb, void *begin_cb_data)
160 {
161   ctx->begin_cb = begin_cb;
162   ctx->begin_cb_data = begin_cb_data;
163   return 0;
164 }
165 
166 int
ijs_server_install_end_cb(IjsServerCtx * ctx,IjsEndJobCb * end_cb,void * end_cb_data)167 ijs_server_install_end_cb (IjsServerCtx *ctx,
168 			   IjsEndJobCb *end_cb, void *end_cb_data)
169 {
170   ctx->end_cb = end_cb;
171   ctx->end_cb_data = end_cb_data;
172   return 0;
173 }
174 
175 int
ijs_server_install_status_cb(IjsServerCtx * ctx,IjsQueryStatusCb * status_cb,void * status_cb_data)176 ijs_server_install_status_cb (IjsServerCtx *ctx,
177 			      IjsQueryStatusCb *status_cb,
178 			      void *status_cb_data)
179 {
180   ctx->status_cb = status_cb;
181   ctx->status_cb_data = status_cb_data;
182   return 0;
183 }
184 
185 int
ijs_server_install_list_cb(IjsServerCtx * ctx,IjsListParamsCb * list_cb,void * list_cb_data)186 ijs_server_install_list_cb (IjsServerCtx *ctx,
187 			   IjsListParamsCb *list_cb, void *list_cb_data)
188 {
189   ctx->list_cb = list_cb;
190   ctx->list_cb_data = list_cb_data;
191   return 0;
192 }
193 
194 int
ijs_server_install_enum_cb(IjsServerCtx * ctx,IjsEnumParamCb * enum_cb,void * enum_cb_data)195 ijs_server_install_enum_cb (IjsServerCtx *ctx,
196 			   IjsEnumParamCb *enum_cb, void *enum_cb_data)
197 {
198   ctx->enum_cb = enum_cb;
199   ctx->enum_cb_data = enum_cb_data;
200   return 0;
201 }
202 
203 int
ijs_server_install_set_cb(IjsServerCtx * ctx,IjsSetParamCb * set_cb,void * set_cb_data)204 ijs_server_install_set_cb (IjsServerCtx *ctx,
205 			   IjsSetParamCb *set_cb, void *set_cb_data)
206 {
207   ctx->set_cb = set_cb;
208   ctx->set_cb_data = set_cb_data;
209   return 0;
210 }
211 
212 int
ijs_server_install_get_cb(IjsServerCtx * ctx,IjsGetParamCb * get_cb,void * get_cb_data)213 ijs_server_install_get_cb (IjsServerCtx *ctx,
214 			   IjsGetParamCb *get_cb, void *get_cb_data)
215 {
216   ctx->get_cb = get_cb;
217   ctx->get_cb_data = get_cb_data;
218   return 0;
219 }
220 
221 static int
ijs_server_ack(IjsServerCtx * ctx)222 ijs_server_ack (IjsServerCtx *ctx)
223 {
224   int status;
225 
226   status = ijs_send_begin (&ctx->send_chan, IJS_CMD_ACK);
227   if (status < 0)
228     return status;
229   return ijs_send_buf (&ctx->send_chan);
230 }
231 
232 void
ijs_server_done(IjsServerCtx * ctx)233 ijs_server_done (IjsServerCtx *ctx)
234 {
235   /* todo: close channels */
236   ijs_server_ack (ctx);
237 
238   free (ctx);
239 }
240 
241 static int
ijs_server_nak(IjsServerCtx * ctx,int errorcode)242 ijs_server_nak (IjsServerCtx *ctx, int errorcode)
243 {
244   int status;
245 
246   status = ijs_send_begin (&ctx->send_chan, IJS_CMD_NAK);
247   if (status < 0)
248     return status;
249   status = ijs_send_int (&ctx->send_chan, errorcode);
250   if (status < 0)
251     return status;
252   return ijs_send_buf (&ctx->send_chan);
253 }
254 
255 /* The return code is: 0 if ok, positive on normal exit, negative on error */
256 typedef int (*ijs_server_proc) (IjsServerCtx *ctx);
257 
258 static int
ijs_server_proc_ack(IjsServerCtx * ctx)259 ijs_server_proc_ack (IjsServerCtx *ctx)
260 {
261   /* servers should not get ack commands */
262   return IJS_EPROTO;
263 }
264 
265 static int
ijs_server_proc_nak(IjsServerCtx * ctx)266 ijs_server_proc_nak (IjsServerCtx *ctx)
267 {
268   /* servers should not get nak commands */
269   return IJS_EPROTO;
270 }
271 
272 static int
ijs_server_proc_ping(IjsServerCtx * ctx)273 ijs_server_proc_ping (IjsServerCtx *ctx)
274 {
275   int status;
276   int version;
277 
278   status = ijs_recv_int (&ctx->recv_chan, &version);
279   if (status < 0)
280     return status;
281   if (version > IJS_VERSION)
282     version = IJS_VERSION;
283   ctx->version = version;
284 
285 #ifdef VERBOSE
286   fprintf (stderr, "ping version=%d\n", version);
287 #endif
288   status = ijs_send_begin (&ctx->send_chan, IJS_CMD_PONG);
289   if (status < 0)
290     return status;
291   status = ijs_send_int (&ctx->send_chan, IJS_VERSION);
292   if (status < 0)
293     return status;
294   return ijs_send_buf (&ctx->send_chan);
295 }
296 
297 static int
ijs_server_proc_pong(IjsServerCtx * ctx)298 ijs_server_proc_pong (IjsServerCtx *ctx)
299 {
300   /* servers should not get pong commands */
301   return IJS_EPROTO;
302 }
303 
304 static int
ijs_server_proc_open(IjsServerCtx * ctx)305 ijs_server_proc_open (IjsServerCtx *ctx)
306 {
307   /* A server might allocate tables here. */
308   return ijs_server_ack (ctx);
309 }
310 
311 static int
ijs_server_proc_close(IjsServerCtx * ctx)312 ijs_server_proc_close (IjsServerCtx *ctx)
313 {
314   /* A server might deallocate memory here. */
315   return ijs_server_ack (ctx);
316 }
317 
318 static int
ijs_server_proc_begin_job(IjsServerCtx * ctx)319 ijs_server_proc_begin_job (IjsServerCtx *ctx)
320 {
321   int code;
322   IjsJobId job_id;
323 
324   code = ijs_recv_int (&ctx->recv_chan, &job_id);
325   if (code < 0) return code;
326 
327   if (ctx->in_job)
328     return ijs_server_nak (ctx, IJS_ETOOMANYJOBS);
329   ctx->in_job = TRUE;
330   ctx->job_id = job_id;
331 
332   return ijs_server_ack (ctx);
333 }
334 
335 static int
ijs_server_proc_end_job(IjsServerCtx * ctx)336 ijs_server_proc_end_job (IjsServerCtx *ctx)
337 {
338   int code;
339   IjsJobId job_id;
340 
341   code = ijs_recv_int (&ctx->recv_chan, &job_id);
342   if (code < 0) return code;
343 
344   if (!ctx->in_job || job_id != ctx->job_id)
345     return ijs_server_nak (ctx, IJS_EJOBID);
346   ctx->in_job = FALSE;
347 
348   return ijs_server_ack (ctx);
349 }
350 
351 static int
ijs_server_proc_cancel_job(IjsServerCtx * ctx)352 ijs_server_proc_cancel_job (IjsServerCtx *ctx)
353 {
354   int code;
355   IjsJobId job_id;
356 
357   code = ijs_recv_int (&ctx->recv_chan, &job_id);
358   if (code < 0) return code;
359 
360   if (!ctx->in_job || job_id != ctx->job_id)
361     return ijs_server_nak (ctx, IJS_EJOBID);
362   /* todo: call cancel callback here */
363   ctx->in_job = FALSE;
364 
365   return ijs_server_ack (ctx);
366 }
367 
368 static int
ijs_server_proc_query_status(IjsServerCtx * ctx)369 ijs_server_proc_query_status (IjsServerCtx *ctx)
370 {
371   int code;
372   IjsJobId job_id;
373 
374   code = ijs_recv_int (&ctx->recv_chan, &job_id);
375   if (code < 0)
376     return code;
377 
378   if (!ctx->in_job || ctx->job_id != job_id)
379     return ijs_server_nak (ctx, IJS_EJOBID);
380 
381   code = ctx->status_cb (ctx->list_cb_data, ctx, job_id);
382   if (code < 0)
383     return ijs_server_nak (ctx, code);
384   else
385     {
386       int status;
387 
388       status = ijs_send_begin (&ctx->send_chan, IJS_CMD_ACK);
389       if (status < 0)
390 	return status;
391       status = ijs_send_int (&ctx->send_chan, code);
392       if (status < 0)
393 	return status;
394       return ijs_send_buf (&ctx->send_chan);
395     }
396 }
397 
398 static int
ijs_server_proc_list_params(IjsServerCtx * ctx)399 ijs_server_proc_list_params (IjsServerCtx *ctx)
400 {
401   int code;
402   char buf[4096];
403   IjsJobId job_id;
404 
405   code = ijs_recv_int (&ctx->recv_chan, &job_id);
406   if (code < 0)
407     return code;
408 
409   if (!ctx->in_job || ctx->job_id != job_id)
410     return ijs_server_nak (ctx, IJS_EJOBID);
411 
412   code = ctx->list_cb (ctx->list_cb_data, ctx, job_id, buf, sizeof(buf));
413   if (code < 0)
414     return ijs_server_nak (ctx, code);
415   else
416     {
417       int status;
418 
419       status = ijs_send_begin (&ctx->send_chan, IJS_CMD_ACK);
420       if (status < 0)
421 	return status;
422       status = ijs_send_block (&ctx->send_chan, buf, code);
423       if (status < 0)
424 	return status;
425       return ijs_send_buf (&ctx->send_chan);
426     }
427 }
428 
429 static int
ijs_server_proc_enum_param(IjsServerCtx * ctx)430 ijs_server_proc_enum_param (IjsServerCtx *ctx)
431 {
432   const char *key;
433   int key_size;
434   int code;
435   char buf[4096];
436   IjsJobId job_id;
437 
438   code = ijs_recv_int (&ctx->recv_chan, &job_id);
439   if (code < 0)
440     return code;
441 
442   if (!ctx->in_job || ctx->job_id != job_id)
443     return ijs_server_nak (ctx, IJS_EJOBID);
444 
445   key = ctx->recv_chan.buf + ctx->recv_chan.buf_idx;
446   key_size = ctx->recv_chan.buf_size - ctx->recv_chan.buf_idx;
447   if (key_size == 0 || key[key_size - 1])
448     return IJS_ESYNTAX;
449 #ifdef VERBOSE
450   fprintf (stderr, "ijs_server_proc_enum_param, key_size = %d\n", key_size);
451 #endif
452 
453   code = ctx->enum_cb (ctx->enum_cb_data, ctx, job_id, key, buf, sizeof(buf));
454   if (code < 0)
455     return ijs_server_nak (ctx, code);
456   else
457     {
458       int status;
459 
460       status = ijs_send_begin (&ctx->send_chan, IJS_CMD_ACK);
461       if (status < 0)
462 	return status;
463       status = ijs_send_block (&ctx->send_chan, buf, code);
464       if (status < 0)
465 	return status;
466       return ijs_send_buf (&ctx->send_chan);
467     }
468 }
469 
470 static int
ijs_strnlen(const char * s,int size)471 ijs_strnlen (const char *s, int size)
472 {
473   int i;
474 
475   for (i = 0; i < size; i++)
476     if (s[i] == 0)
477       return i;
478   return size;
479 }
480 
481 static int
ijs_server_parse_int(const char * value,int size,int * result)482 ijs_server_parse_int (const char *value, int size, int *result)
483 {
484   int num = 0;
485   int i;
486   int sign = 1;
487 
488   i = 0;
489   if (i == size)
490     return IJS_ESYNTAX;
491   if (value[i] == '-')
492     {
493       sign = -1;
494       i++;
495     }
496 
497   if (i == size)
498     return IJS_ESYNTAX;
499 
500   for (; i < size; i++)
501     {
502       char c = value[i];
503       if (c < '0' || c > '9')
504 	return IJS_ESYNTAX;
505       num = (num * 10) + (c - '0');
506     }
507   *result = num;
508   return 0;
509 }
510 
511 static int
ijs_server_parse_float(const char * value,int size,double * result)512 ijs_server_parse_float (const char *value, int size, double *result)
513 {
514   char buf[256];
515   char *tail;
516 
517   if (size + 1 > sizeof(buf))
518     return IJS_EBUF;
519   memcpy (buf, value, size);
520   buf[size] = 0;
521   *result = strtod (buf, &tail);
522   if (tail == buf)
523     return IJS_ESYNTAX;
524   return 0;
525 }
526 
527 static int
ijs_server_set_param(IjsServerCtx * ctx,IjsJobId job_id,const char * key,const char * value,int value_size)528 ijs_server_set_param (IjsServerCtx *ctx, IjsJobId job_id, const char *key,
529 		      const char *value, int value_size)
530 {
531   int code;
532 
533 #ifdef VERBOSE
534   fprintf (stderr, "set_param %s = ", key);
535   fwrite (value, 1, value_size, stderr);
536   fputs ("\n", stderr);
537 #endif
538   if (!strcmp (key, "NumChan"))
539     {
540       code = ijs_server_parse_int (value, value_size, &ctx->ph->n_chan);
541       if (code == 0)
542 	ctx->fields_set |= IJS_N_CHAN_SET;
543       return code;
544     }
545   else if (!strcmp (key, "BitsPerSample"))
546     {
547       code = ijs_server_parse_int (value, value_size, &ctx->ph->bps);
548       if (code == 0)
549 	ctx->fields_set |= IJS_BPS_SET;
550       return code;
551     }
552   else if (!strcmp (key, "ColorSpace"))
553     {
554       int size = value_size;
555 
556       if (size < (int)sizeof(ctx->ph->cs) - 1)
557 	size = sizeof(ctx->ph->cs) - 1;
558       memcpy (ctx->ph->cs, value, size);
559       ctx->ph->cs[size] = 0;
560 	ctx->fields_set |= IJS_CS_SET;
561       return 0;
562     }
563   else if (!strcmp (key, "Width"))
564     {
565       code = ijs_server_parse_int (value, value_size, &ctx->ph->width);
566       if (code == 0)
567 	ctx->fields_set |= IJS_WIDTH_SET;
568       return code;
569     }
570   else if (!strcmp (key, "Height"))
571     {
572       code = ijs_server_parse_int (value, value_size, &ctx->ph->height);
573       if (code == 0)
574 	ctx->fields_set |= IJS_HEIGHT_SET;
575       return code;
576     }
577   else if (!strcmp (key, "Dpi"))
578     {
579       int x_ix;
580 
581       for (x_ix = 0; x_ix < value_size; x_ix++)
582 	if (value[x_ix] == 'x')
583 	  break;
584       if (x_ix == value_size)
585 	return IJS_ESYNTAX;
586       code = ijs_server_parse_float (value, x_ix, &ctx->ph->xres);
587       if (code < 0)
588 	return code;
589       code = ijs_server_parse_float (value + x_ix + 1, value_size - (x_ix + 1),
590 				     &ctx->ph->yres);
591       if (code < 0)
592 	return code;
593       ctx->fields_set |= IJS_DPI_SET;
594       return 0;
595     }
596   else
597     {
598       return ctx->set_cb (ctx->set_cb_data, ctx, job_id, key, value, value_size);
599     }
600 }
601 
602 static int
ijs_server_proc_set_param(IjsServerCtx * ctx)603 ijs_server_proc_set_param (IjsServerCtx *ctx)
604 {
605   const char *key, *value;
606   int key_size, value_size;
607   IjsJobId job_id;
608   int param_size;
609   int code;
610 
611   code = ijs_recv_int (&ctx->recv_chan, &job_id);
612   if (code < 0)
613     return code;
614 
615   if (!ctx->in_job || ctx->job_id != job_id)
616     return ijs_server_nak (ctx, IJS_EJOBID);
617 
618   code = ijs_recv_int (&ctx->recv_chan, &param_size);
619   if (code < 0)
620     return code;
621   if (param_size != ctx->recv_chan.buf_size - ctx->recv_chan.buf_idx)
622       return IJS_EPROTO;
623   key = ctx->recv_chan.buf + ctx->recv_chan.buf_idx;
624   key_size = ijs_strnlen (key, ctx->recv_chan.buf_size);
625   if (key_size == param_size)
626     return IJS_EPROTO;
627   value = key + key_size + 1;
628   value_size = param_size - (key_size + 1);
629   code = ijs_server_set_param (ctx, job_id, key, value, value_size);
630   if (code)
631     return ijs_server_nak (ctx, code);
632   else
633     return ijs_server_ack (ctx);
634 }
635 
636 static int
ijs_server_get_param(IjsServerCtx * ctx,IjsJobId job_id,const char * key,char * value,int value_size)637 ijs_server_get_param (IjsServerCtx *ctx, IjsJobId job_id, const char *key,
638 		      char *value, int value_size)
639 {
640 #ifdef VERBOSE
641   fprintf (stderr, "ijs_server_get_param %s\n", key);
642 #endif
643   return ctx->get_cb (ctx->get_cb_data, ctx, job_id, key,
644 		      value, value_size);
645 }
646 
647 static int
ijs_server_proc_get_param(IjsServerCtx * ctx)648 ijs_server_proc_get_param (IjsServerCtx *ctx)
649 {
650   const char *key;
651   int key_size;
652   int code;
653   char buf[4096];
654   IjsJobId job_id;
655 
656   code = ijs_recv_int (&ctx->recv_chan, &job_id);
657   if (code < 0)
658     return code;
659 
660   if (!ctx->in_job || ctx->job_id != job_id)
661     return ijs_server_nak (ctx, IJS_EJOBID);
662 
663   key = ctx->recv_chan.buf + ctx->recv_chan.buf_idx;
664   key_size = ctx->recv_chan.buf_size - ctx->recv_chan.buf_idx;
665   if (key_size == 0 || key[key_size - 1])
666     return IJS_ESYNTAX;
667 #ifdef VERBOSE
668   fprintf (stderr, "ijs_server_proc_get_param, key_size = %d\n", key_size);
669 #endif
670 
671   code = ijs_server_get_param (ctx, job_id, key, buf, sizeof(buf));
672   if (code < 0)
673     return ijs_server_nak (ctx, code);
674   else
675     {
676       int status;
677 
678       status = ijs_send_begin (&ctx->send_chan, IJS_CMD_ACK);
679       if (status < 0)
680 	return status;
681       status = ijs_send_block (&ctx->send_chan, buf, code);
682       if (status < 0)
683 	return status;
684       return ijs_send_buf (&ctx->send_chan);
685     }
686 }
687 
688 static int
ijs_server_proc_begin_page(IjsServerCtx * ctx)689 ijs_server_proc_begin_page (IjsServerCtx *ctx)
690 {
691   IjsPageHeader *ph = ctx->ph;
692   int status = 0;
693 
694   if (ph == NULL)
695     status = IJS_EPROTO;
696   if ((ctx->fields_set & IJS_FIELDS_REQUIRED) != IJS_FIELDS_REQUIRED)
697     status = IJS_EPROTO;
698 #ifdef VERBOSE
699   fprintf (stderr, "begin page %d %d %d %d %d\n",
700 	   ph->n_chan, ph->bps, ph->cs, ph->width, ph->height);
701 #endif
702   if (!status)
703     {
704       ctx->in_page = TRUE;
705       return ijs_server_ack (ctx);
706     }
707   else
708     return ijs_server_nak (ctx, status);
709 }
710 
711 static int
ijs_server_read_data(IjsServerCtx * ctx,char * buf,int size)712 ijs_server_read_data (IjsServerCtx *ctx, char *buf, int size)
713 {
714   int status;
715 
716   status = ijs_recv_read (&ctx->recv_chan, buf, size);
717   return (status == size) ? 0 : IJS_EIO;
718 }
719 
720 static int
ijs_server_proc_send_data_block(IjsServerCtx * ctx)721 ijs_server_proc_send_data_block (IjsServerCtx *ctx)
722 {
723   int size;
724   int status = 0;
725   IjsJobId job_id;
726 
727   status = ijs_recv_int (&ctx->recv_chan, &job_id);
728   if (status < 0) return status;
729 
730   if (!ctx->in_job || job_id != ctx->job_id)
731     status = IJS_EJOBID;
732   else if (ctx->buf == NULL)
733     status = IJS_EPROTO;
734 
735   if (!status) status = ijs_recv_int (&ctx->recv_chan, &size);
736 
737 #ifdef VERBOSE
738   fprintf (stderr, "status=%d, send data block id=%d, size=%d\n",
739 	   status, job_id, size);
740 #endif
741   if (status)
742     return ijs_server_nak (ctx, status);
743 
744   if (size <= ctx->buf_size - ctx->buf_ix)
745     {
746       status = ijs_server_read_data (ctx, ctx->buf + ctx->buf_ix, size);
747       ctx->buf_ix += size;
748     }
749   else
750     {
751       ctx->overflow_buf_size = size - (ctx->buf_size - ctx->buf_ix);
752       ctx->overflow_buf = (char *)malloc (ctx->overflow_buf_size);
753       ctx->overflow_buf_ix = 0;
754       status = ijs_server_read_data (ctx, ctx->buf + ctx->buf_ix,
755 				     ctx->buf_size - ctx->buf_ix);
756       ctx->buf_ix = ctx->buf_size;
757       if (!status)
758 	{
759 	  status = ijs_server_read_data (ctx, ctx->overflow_buf,
760 					 ctx->overflow_buf_size);
761 	}
762     }
763   return ijs_server_ack (ctx);
764 }
765 
766 static int
ijs_server_proc_end_page(IjsServerCtx * ctx)767 ijs_server_proc_end_page (IjsServerCtx *ctx)
768 {
769 #ifdef VERBOSE
770   fprintf (stderr, "end page\n");
771 #endif
772   return ijs_server_ack (ctx);
773 }
774 
775 static int
ijs_server_proc_exit(IjsServerCtx * ctx)776 ijs_server_proc_exit (IjsServerCtx *ctx)
777 {
778   return 1;
779 }
780 
781 ijs_server_proc ijs_server_procs[] = {
782   ijs_server_proc_ack,
783   ijs_server_proc_nak,
784   ijs_server_proc_ping,
785   ijs_server_proc_pong,
786   ijs_server_proc_open,
787   ijs_server_proc_close,
788   ijs_server_proc_begin_job,
789   ijs_server_proc_end_job,
790   ijs_server_proc_cancel_job,
791   ijs_server_proc_query_status,
792   ijs_server_proc_list_params,
793   ijs_server_proc_enum_param,
794   ijs_server_proc_set_param,
795   ijs_server_proc_get_param,
796   ijs_server_proc_begin_page,
797   ijs_server_proc_send_data_block,
798   ijs_server_proc_end_page,
799   ijs_server_proc_exit
800 };
801 
802 int
ijs_server_iter(IjsServerCtx * ctx)803 ijs_server_iter (IjsServerCtx *ctx)
804 {
805   int cmd_num;
806   int status;
807 
808   status = ijs_recv_buf (&ctx->recv_chan);
809 
810   if (status < 0)
811     return status;
812 
813   cmd_num = ijs_get_int (ctx->recv_chan.buf);
814 #ifdef VERBOSE
815   fprintf (stderr, "command %d, %d bytes\n", cmd_num, ctx->recv_chan.buf_size);
816 #endif
817   if (cmd_num < 0 ||
818       cmd_num >= (int)sizeof(ijs_server_procs) / sizeof(ijs_server_procs[0]))
819     return -1;
820   return ijs_server_procs[cmd_num] (ctx);
821 }
822 
823 /**
824  * ijs_server_get_page_header: Get the page header.
825  * @ctx: The server context.
826  * @ph: Where to store the page header.
827  *
828  * Return value: 0 on success, 1 on normal exit, negative on error.
829  **/
830 int
ijs_server_get_page_header(IjsServerCtx * ctx,IjsPageHeader * ph)831 ijs_server_get_page_header (IjsServerCtx *ctx, IjsPageHeader *ph)
832 {
833   int status;
834 
835   ctx->ph = ph;
836   ctx->in_page = FALSE;
837 
838   do
839     {
840       status = ijs_server_iter (ctx);
841     }
842   while (status == 0 && !ctx->in_page);
843 
844   ctx->ph = NULL;
845   return status;
846 }
847 
848 /**
849  * ijs_server_get_data: Get data from client.
850  * @ctx: The server context.
851  * @buf: Buffer for data being read.
852  * @size: Size of buf.
853  *
854  * Gets data from client. Data is stored in @buf or the
855  * overflow_buf.
856  *
857  * Return value: Number of bytes read, -1 on end of page, or < 0 on
858  * error.
859  **/
860 int
ijs_server_get_data(IjsServerCtx * ctx,char * buf,int size)861 ijs_server_get_data (IjsServerCtx *ctx, char *buf, int size)
862 {
863   int buf_ix = 0;
864   int status = 0;
865 
866 #ifdef VERBOSE
867   fprintf (stderr, "ijs_server_get_data %d\n", size);
868 #endif
869 
870   if (ctx->overflow_buf != NULL)
871     {
872       int n_bytes = ctx->overflow_buf_size - ctx->overflow_buf_ix;
873       if (n_bytes > size)
874 	n_bytes = size;
875       memcpy (buf, ctx->overflow_buf + ctx->overflow_buf_ix, n_bytes);
876       ctx->overflow_buf_ix += n_bytes;
877       buf_ix = n_bytes;
878       if (ctx->overflow_buf_ix == ctx->overflow_buf_size)
879 	{
880 	  free (ctx->overflow_buf);
881 	  ctx->overflow_buf = NULL;
882 	  ctx->overflow_buf_size = 0;
883 	  ctx->overflow_buf_ix = 0;
884 	}
885     }
886   ctx->buf = buf;
887   ctx->buf_size = size;
888   ctx->buf_ix = buf_ix;
889   while (!status && ctx->buf_ix < size)
890     {
891       status = ijs_server_iter (ctx);
892     }
893   ctx->buf = NULL;
894   return status;
895 }
896