1 /**
2  * Copyright (C) 2001-2004 Artifex Software, Inc.
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   /* MSVC    defines _MSC_VER     and _WIN32
119      Mingw   defines __MINGW32__  and _WIN32
120      Watcom  defines __WATCOMC__  and _WIN32
121      borland defines __BORLANDC__ and _WIN32 */
122 #ifdef _WIN32
123 #if !defined(__BORLANDC__)
124   /* MSVC, mingw, watcom */
125   /* mingw and watcom have both _setmode and setmode */
126   _setmode(fd_from, _O_BINARY);
127   _setmode(fd_to, _O_BINARY);
128 #else
129   /* borland has setmode but not _setmode */
130   setmode(fd_from, _O_BINARY);
131   setmode(fd_to, _O_BINARY);
132 #endif /* !__BORLANDC__ */
133 #endif
134 #ifdef VERBOSE
135   fprintf (stderr, "fd_from = %d, fd_to = %d\n", fd_from, fd_to);
136 #endif
137   ijs_recv_init (&ctx->recv_chan, fd_from);
138   ijs_send_init (&ctx->send_chan, fd_to);
139   nbytes = read (ctx->recv_chan.fd, helo_buf, sizeof(helo_buf));
140 
141   if (nbytes != sizeof(helo_buf))
142     ok = FALSE;
143 
144   if (ok)
145     nbytes = write (ctx->send_chan.fd, resp_buf, sizeof(resp_buf));
146   if (nbytes != sizeof(resp_buf))
147     ok = FALSE;
148 
149   ctx->in_job = FALSE;
150   ctx->job_id = -1;
151 
152   ctx->ph = NULL;
153   ctx->in_page = FALSE;
154   ctx->buf = NULL;
155   ctx->overflow_buf = NULL;
156 
157   ctx->begin_cb = ijs_server_dummy_begin_cb;
158   ctx->end_cb = ijs_server_dummy_end_cb;
159 
160   if (ok)
161     return ctx;
162   else
163     {
164       ijs_server_done (ctx);
165       return NULL;
166     }
167 }
168 
169 int
ijs_server_install_begin_cb(IjsServerCtx * ctx,IjsBeginJobCb * begin_cb,void * begin_cb_data)170 ijs_server_install_begin_cb (IjsServerCtx *ctx,
171 			     IjsBeginJobCb *begin_cb, void *begin_cb_data)
172 {
173   ctx->begin_cb = begin_cb;
174   ctx->begin_cb_data = begin_cb_data;
175   return 0;
176 }
177 
178 int
ijs_server_install_end_cb(IjsServerCtx * ctx,IjsEndJobCb * end_cb,void * end_cb_data)179 ijs_server_install_end_cb (IjsServerCtx *ctx,
180 			   IjsEndJobCb *end_cb, void *end_cb_data)
181 {
182   ctx->end_cb = end_cb;
183   ctx->end_cb_data = end_cb_data;
184   return 0;
185 }
186 
187 int
ijs_server_install_status_cb(IjsServerCtx * ctx,IjsQueryStatusCb * status_cb,void * status_cb_data)188 ijs_server_install_status_cb (IjsServerCtx *ctx,
189 			      IjsQueryStatusCb *status_cb,
190 			      void *status_cb_data)
191 {
192   ctx->status_cb = status_cb;
193   ctx->status_cb_data = status_cb_data;
194   return 0;
195 }
196 
197 int
ijs_server_install_list_cb(IjsServerCtx * ctx,IjsListParamsCb * list_cb,void * list_cb_data)198 ijs_server_install_list_cb (IjsServerCtx *ctx,
199 			   IjsListParamsCb *list_cb, void *list_cb_data)
200 {
201   ctx->list_cb = list_cb;
202   ctx->list_cb_data = list_cb_data;
203   return 0;
204 }
205 
206 int
ijs_server_install_enum_cb(IjsServerCtx * ctx,IjsEnumParamCb * enum_cb,void * enum_cb_data)207 ijs_server_install_enum_cb (IjsServerCtx *ctx,
208 			   IjsEnumParamCb *enum_cb, void *enum_cb_data)
209 {
210   ctx->enum_cb = enum_cb;
211   ctx->enum_cb_data = enum_cb_data;
212   return 0;
213 }
214 
215 int
ijs_server_install_set_cb(IjsServerCtx * ctx,IjsSetParamCb * set_cb,void * set_cb_data)216 ijs_server_install_set_cb (IjsServerCtx *ctx,
217 			   IjsSetParamCb *set_cb, void *set_cb_data)
218 {
219   ctx->set_cb = set_cb;
220   ctx->set_cb_data = set_cb_data;
221   return 0;
222 }
223 
224 int
ijs_server_install_get_cb(IjsServerCtx * ctx,IjsGetParamCb * get_cb,void * get_cb_data)225 ijs_server_install_get_cb (IjsServerCtx *ctx,
226 			   IjsGetParamCb *get_cb, void *get_cb_data)
227 {
228   ctx->get_cb = get_cb;
229   ctx->get_cb_data = get_cb_data;
230   return 0;
231 }
232 
233 static int
ijs_server_ack(IjsServerCtx * ctx)234 ijs_server_ack (IjsServerCtx *ctx)
235 {
236   int status;
237 
238   status = ijs_send_begin (&ctx->send_chan, IJS_CMD_ACK);
239   if (status < 0)
240     return status;
241   return ijs_send_buf (&ctx->send_chan);
242 }
243 
244 void
ijs_server_done(IjsServerCtx * ctx)245 ijs_server_done (IjsServerCtx *ctx)
246 {
247   /* todo: close channels */
248   ijs_server_ack (ctx);
249 
250   free (ctx);
251 }
252 
253 static int
ijs_server_nak(IjsServerCtx * ctx,int errorcode)254 ijs_server_nak (IjsServerCtx *ctx, int errorcode)
255 {
256   int status;
257 
258   status = ijs_send_begin (&ctx->send_chan, IJS_CMD_NAK);
259   if (status < 0)
260     return status;
261   status = ijs_send_int (&ctx->send_chan, errorcode);
262   if (status < 0)
263     return status;
264   return ijs_send_buf (&ctx->send_chan);
265 }
266 
267 /* The return code is: 0 if ok, positive on normal exit, negative on error */
268 typedef int (*ijs_server_proc) (IjsServerCtx *ctx);
269 
270 static int
ijs_server_proc_ack(IjsServerCtx * ctx)271 ijs_server_proc_ack (IjsServerCtx *ctx)
272 {
273   /* servers should not get ack commands */
274   return IJS_EPROTO;
275 }
276 
277 static int
ijs_server_proc_nak(IjsServerCtx * ctx)278 ijs_server_proc_nak (IjsServerCtx *ctx)
279 {
280   /* servers should not get nak commands */
281   return IJS_EPROTO;
282 }
283 
284 static int
ijs_server_proc_ping(IjsServerCtx * ctx)285 ijs_server_proc_ping (IjsServerCtx *ctx)
286 {
287   int status;
288   int version;
289 
290   status = ijs_recv_int (&ctx->recv_chan, &version);
291   if (status < 0)
292     return status;
293   if (version > IJS_VERSION)
294     version = IJS_VERSION;
295   ctx->version = version;
296 
297 #ifdef VERBOSE
298   fprintf (stderr, "ping version=%d\n", version);
299 #endif
300   status = ijs_send_begin (&ctx->send_chan, IJS_CMD_PONG);
301   if (status < 0)
302     return status;
303   status = ijs_send_int (&ctx->send_chan, IJS_VERSION);
304   if (status < 0)
305     return status;
306   return ijs_send_buf (&ctx->send_chan);
307 }
308 
309 static int
ijs_server_proc_pong(IjsServerCtx * ctx)310 ijs_server_proc_pong (IjsServerCtx *ctx)
311 {
312   /* servers should not get pong commands */
313   return IJS_EPROTO;
314 }
315 
316 static int
ijs_server_proc_open(IjsServerCtx * ctx)317 ijs_server_proc_open (IjsServerCtx *ctx)
318 {
319   /* A server might allocate tables here. */
320   return ijs_server_ack (ctx);
321 }
322 
323 static int
ijs_server_proc_close(IjsServerCtx * ctx)324 ijs_server_proc_close (IjsServerCtx *ctx)
325 {
326   /* A server might deallocate memory here. */
327   return ijs_server_ack (ctx);
328 }
329 
330 static int
ijs_server_proc_begin_job(IjsServerCtx * ctx)331 ijs_server_proc_begin_job (IjsServerCtx *ctx)
332 {
333   int code;
334   IjsJobId job_id;
335 
336   code = ijs_recv_int (&ctx->recv_chan, &job_id);
337   if (code < 0) return code;
338 
339   if (ctx->in_job)
340     return ijs_server_nak (ctx, IJS_ETOOMANYJOBS);
341   ctx->in_job = TRUE;
342   ctx->job_id = job_id;
343 
344   return ijs_server_ack (ctx);
345 }
346 
347 static int
ijs_server_proc_end_job(IjsServerCtx * ctx)348 ijs_server_proc_end_job (IjsServerCtx *ctx)
349 {
350   int code;
351   IjsJobId job_id;
352 
353   code = ijs_recv_int (&ctx->recv_chan, &job_id);
354   if (code < 0) return code;
355 
356   if (!ctx->in_job || job_id != ctx->job_id)
357     return ijs_server_nak (ctx, IJS_EJOBID);
358   ctx->in_job = FALSE;
359 
360   return ijs_server_ack (ctx);
361 }
362 
363 static int
ijs_server_proc_cancel_job(IjsServerCtx * ctx)364 ijs_server_proc_cancel_job (IjsServerCtx *ctx)
365 {
366   int code;
367   IjsJobId job_id;
368 
369   code = ijs_recv_int (&ctx->recv_chan, &job_id);
370   if (code < 0) return code;
371 
372   if (!ctx->in_job || job_id != ctx->job_id)
373     return ijs_server_nak (ctx, IJS_EJOBID);
374   /* todo: call cancel callback here */
375   ctx->in_job = FALSE;
376 
377   return ijs_server_ack (ctx);
378 }
379 
380 static int
ijs_server_proc_query_status(IjsServerCtx * ctx)381 ijs_server_proc_query_status (IjsServerCtx *ctx)
382 {
383   int code;
384   IjsJobId job_id;
385 
386   code = ijs_recv_int (&ctx->recv_chan, &job_id);
387   if (code < 0)
388     return code;
389 
390   if (!ctx->in_job || ctx->job_id != job_id)
391     return ijs_server_nak (ctx, IJS_EJOBID);
392 
393   code = ctx->status_cb (ctx->list_cb_data, ctx, job_id);
394   if (code < 0)
395     return ijs_server_nak (ctx, code);
396   else
397     {
398       int status;
399 
400       status = ijs_send_begin (&ctx->send_chan, IJS_CMD_ACK);
401       if (status < 0)
402 	return status;
403       status = ijs_send_int (&ctx->send_chan, code);
404       if (status < 0)
405 	return status;
406       return ijs_send_buf (&ctx->send_chan);
407     }
408 }
409 
410 static int
ijs_server_proc_list_params(IjsServerCtx * ctx)411 ijs_server_proc_list_params (IjsServerCtx *ctx)
412 {
413   int code;
414   char buf[4096];
415   IjsJobId job_id;
416 
417   code = ijs_recv_int (&ctx->recv_chan, &job_id);
418   if (code < 0)
419     return code;
420 
421   if (!ctx->in_job || ctx->job_id != job_id)
422     return ijs_server_nak (ctx, IJS_EJOBID);
423 
424   code = ctx->list_cb (ctx->list_cb_data, ctx, job_id, buf, sizeof(buf));
425   if (code < 0)
426     return ijs_server_nak (ctx, code);
427   else
428     {
429       int status;
430 
431       status = ijs_send_begin (&ctx->send_chan, IJS_CMD_ACK);
432       if (status < 0)
433 	return status;
434       status = ijs_send_block (&ctx->send_chan, buf, code);
435       if (status < 0)
436 	return status;
437       return ijs_send_buf (&ctx->send_chan);
438     }
439 }
440 
441 static int
ijs_server_proc_enum_param(IjsServerCtx * ctx)442 ijs_server_proc_enum_param (IjsServerCtx *ctx)
443 {
444   const char *key;
445   int key_size;
446   int code;
447   char buf[4096];
448   IjsJobId job_id;
449 
450   code = ijs_recv_int (&ctx->recv_chan, &job_id);
451   if (code < 0)
452     return code;
453 
454   if (!ctx->in_job || ctx->job_id != job_id)
455     return ijs_server_nak (ctx, IJS_EJOBID);
456 
457   key = ctx->recv_chan.buf + ctx->recv_chan.buf_idx;
458   key_size = ctx->recv_chan.buf_size - ctx->recv_chan.buf_idx;
459   if (key_size == 0 || key[key_size - 1])
460     return IJS_ESYNTAX;
461 #ifdef VERBOSE
462   fprintf (stderr, "ijs_server_proc_enum_param, key_size = %d\n", key_size);
463 #endif
464 
465   code = ctx->enum_cb (ctx->enum_cb_data, ctx, job_id, key, buf, sizeof(buf));
466   if (code < 0)
467     return ijs_server_nak (ctx, code);
468   else
469     {
470       int status;
471 
472       status = ijs_send_begin (&ctx->send_chan, IJS_CMD_ACK);
473       if (status < 0)
474 	return status;
475       status = ijs_send_block (&ctx->send_chan, buf, code);
476       if (status < 0)
477 	return status;
478       return ijs_send_buf (&ctx->send_chan);
479     }
480 }
481 
482 static int
ijs_strnlen(const char * s,int size)483 ijs_strnlen (const char *s, int size)
484 {
485   int i;
486 
487   for (i = 0; i < size; i++)
488     if (s[i] == 0)
489       return i;
490   return size;
491 }
492 
493 static int
ijs_server_parse_int(const char * value,int size,int * result)494 ijs_server_parse_int (const char *value, int size, int *result)
495 {
496   int num = 0;
497   int i;
498   int sign = 1;
499 
500   i = 0;
501   if (i == size)
502     return IJS_ESYNTAX;
503   if (value[i] == '-')
504     {
505       sign = -1;
506       i++;
507     }
508 
509   if (i == size)
510     return IJS_ESYNTAX;
511 
512   for (; i < size; i++)
513     {
514       char c = value[i];
515       if (c < '0' || c > '9')
516 	return IJS_ESYNTAX;
517       num = (num * 10) + (c - '0');
518     }
519   *result = num;
520   return 0;
521 }
522 
523 static int
ijs_server_parse_float(const char * value,int size,double * result)524 ijs_server_parse_float (const char *value, int size, double *result)
525 {
526   char buf[256];
527   char *tail;
528 
529   if (size + 1 > sizeof(buf))
530     return IJS_EBUF;
531   memcpy (buf, value, size);
532   buf[size] = 0;
533   *result = strtod (buf, &tail);
534   if (tail == buf)
535     return IJS_ESYNTAX;
536   return 0;
537 }
538 
539 static int
ijs_server_set_param(IjsServerCtx * ctx,IjsJobId job_id,const char * key,const char * value,int value_size)540 ijs_server_set_param (IjsServerCtx *ctx, IjsJobId job_id, const char *key,
541 		      const char *value, int value_size)
542 {
543   int code;
544 
545 #ifdef VERBOSE
546   fprintf (stderr, "set_param %s = ", key);
547   fwrite (value, 1, value_size, stderr);
548   fputs ("\n", stderr);
549 #endif
550   if (!strcmp (key, "NumChan"))
551     {
552       code = ijs_server_parse_int (value, value_size, &ctx->ph->n_chan);
553       if (code == 0)
554 	ctx->fields_set |= IJS_N_CHAN_SET;
555       return code;
556     }
557   else if (!strcmp (key, "BitsPerSample"))
558     {
559       code = ijs_server_parse_int (value, value_size, &ctx->ph->bps);
560       if (code == 0)
561 	ctx->fields_set |= IJS_BPS_SET;
562       return code;
563     }
564   else if (!strcmp (key, "ColorSpace"))
565     {
566       int size = value_size;
567 
568       if (size > (int)sizeof(ctx->ph->cs) - 1)
569 	size = sizeof(ctx->ph->cs) - 1;
570       memcpy (ctx->ph->cs, value, size);
571       ctx->ph->cs[size] = 0;
572 	ctx->fields_set |= IJS_CS_SET;
573       return 0;
574     }
575   else if (!strcmp (key, "Width"))
576     {
577       code = ijs_server_parse_int (value, value_size, &ctx->ph->width);
578       if (code == 0)
579 	ctx->fields_set |= IJS_WIDTH_SET;
580       return code;
581     }
582   else if (!strcmp (key, "Height"))
583     {
584       code = ijs_server_parse_int (value, value_size, &ctx->ph->height);
585       if (code == 0)
586 	ctx->fields_set |= IJS_HEIGHT_SET;
587       return code;
588     }
589   else if (!strcmp (key, "Dpi"))
590     {
591       int x_ix;
592 
593       for (x_ix = 0; x_ix < value_size; x_ix++)
594 	if (value[x_ix] == 'x')
595 	  break;
596       if (x_ix == value_size)
597 	return IJS_ESYNTAX;
598       code = ijs_server_parse_float (value, x_ix, &ctx->ph->xres);
599       if (code < 0)
600 	return code;
601       code = ijs_server_parse_float (value + x_ix + 1, value_size - (x_ix + 1),
602 				     &ctx->ph->yres);
603       if (code < 0)
604 	return code;
605       ctx->fields_set |= IJS_DPI_SET;
606       return 0;
607     }
608   else
609     {
610       return ctx->set_cb (ctx->set_cb_data, ctx, job_id, key, value, value_size);
611     }
612 }
613 
614 static int
ijs_server_proc_set_param(IjsServerCtx * ctx)615 ijs_server_proc_set_param (IjsServerCtx *ctx)
616 {
617   const char *key, *value;
618   int key_size, value_size;
619   IjsJobId job_id;
620   int param_size;
621   int code;
622 
623   code = ijs_recv_int (&ctx->recv_chan, &job_id);
624   if (code < 0)
625     return code;
626 
627   if (!ctx->in_job || ctx->job_id != job_id)
628     return ijs_server_nak (ctx, IJS_EJOBID);
629 
630   code = ijs_recv_int (&ctx->recv_chan, &param_size);
631   if (code < 0)
632     return code;
633   if (param_size != ctx->recv_chan.buf_size - ctx->recv_chan.buf_idx)
634       return IJS_EPROTO;
635   key = ctx->recv_chan.buf + ctx->recv_chan.buf_idx;
636   key_size = ijs_strnlen (key, ctx->recv_chan.buf_size);
637   if (key_size == param_size)
638     return IJS_EPROTO;
639   value = key + key_size + 1;
640   value_size = param_size - (key_size + 1);
641   code = ijs_server_set_param (ctx, job_id, key, value, value_size);
642   if (code)
643     return ijs_server_nak (ctx, code);
644   else
645     return ijs_server_ack (ctx);
646 }
647 
648 static int
ijs_server_get_param(IjsServerCtx * ctx,IjsJobId job_id,const char * key,char * value,int value_size)649 ijs_server_get_param (IjsServerCtx *ctx, IjsJobId job_id, const char *key,
650 		      char *value, int value_size)
651 {
652 #ifdef VERBOSE
653   fprintf (stderr, "ijs_server_get_param %s\n", key);
654 #endif
655   return ctx->get_cb (ctx->get_cb_data, ctx, job_id, key,
656 		      value, value_size);
657 }
658 
659 static int
ijs_server_proc_get_param(IjsServerCtx * ctx)660 ijs_server_proc_get_param (IjsServerCtx *ctx)
661 {
662   const char *key;
663   int key_size;
664   int code;
665   char buf[4096];
666   IjsJobId job_id;
667 
668   code = ijs_recv_int (&ctx->recv_chan, &job_id);
669   if (code < 0)
670     return code;
671 
672   if (!ctx->in_job || ctx->job_id != job_id)
673     return ijs_server_nak (ctx, IJS_EJOBID);
674 
675   key = ctx->recv_chan.buf + ctx->recv_chan.buf_idx;
676   key_size = ctx->recv_chan.buf_size - ctx->recv_chan.buf_idx;
677   if (key_size == 0 || key[key_size - 1])
678     return IJS_ESYNTAX;
679 #ifdef VERBOSE
680   fprintf (stderr, "ijs_server_proc_get_param, key_size = %d\n", key_size);
681 #endif
682 
683   code = ijs_server_get_param (ctx, job_id, key, buf, sizeof(buf));
684   if (code < 0)
685     return ijs_server_nak (ctx, code);
686   else
687     {
688       int status;
689 
690       status = ijs_send_begin (&ctx->send_chan, IJS_CMD_ACK);
691       if (status < 0)
692 	return status;
693       status = ijs_send_block (&ctx->send_chan, buf, code);
694       if (status < 0)
695 	return status;
696       return ijs_send_buf (&ctx->send_chan);
697     }
698 }
699 
700 static int
ijs_server_proc_begin_page(IjsServerCtx * ctx)701 ijs_server_proc_begin_page (IjsServerCtx *ctx)
702 {
703   IjsPageHeader *ph = ctx->ph;
704   int status = 0;
705 
706   if (ph == NULL)
707     status = IJS_EPROTO;
708   if ((ctx->fields_set & IJS_FIELDS_REQUIRED) != IJS_FIELDS_REQUIRED)
709     status = IJS_EPROTO;
710 #ifdef VERBOSE
711   fprintf (stderr, "begin page %d %d %s %d %d\n",
712 	   ph->n_chan, ph->bps, ph->cs, ph->width, ph->height);
713 #endif
714   if (!status)
715     {
716       ctx->in_page = TRUE;
717       return ijs_server_ack (ctx);
718     }
719   else
720     return ijs_server_nak (ctx, status);
721 }
722 
723 static int
ijs_server_read_data(IjsServerCtx * ctx,char * buf,int size)724 ijs_server_read_data (IjsServerCtx *ctx, char *buf, int size)
725 {
726   int status;
727 
728   status = ijs_recv_read (&ctx->recv_chan, buf, size);
729   return (status == size) ? 0 : IJS_EIO;
730 }
731 
732 static int
ijs_server_proc_send_data_block(IjsServerCtx * ctx)733 ijs_server_proc_send_data_block (IjsServerCtx *ctx)
734 {
735   int size;
736   int status = 0;
737   IjsJobId job_id;
738 
739   status = ijs_recv_int (&ctx->recv_chan, &job_id);
740   if (status < 0) return status;
741 
742   if (!ctx->in_job || job_id != ctx->job_id)
743     status = IJS_EJOBID;
744   else if (ctx->buf == NULL)
745     status = IJS_EPROTO;
746 
747   if (!status) status = ijs_recv_int (&ctx->recv_chan, &size);
748 
749 #ifdef VERBOSE
750   fprintf (stderr, "status=%d, send data block id=%d, size=%d\n",
751 	   status, job_id, size);
752 #endif
753   if (status)
754     return ijs_server_nak (ctx, status);
755 
756   if (size <= ctx->buf_size - ctx->buf_ix)
757     {
758       status = ijs_server_read_data (ctx, ctx->buf + ctx->buf_ix, size);
759       ctx->buf_ix += size;
760     }
761   else
762     {
763       ctx->overflow_buf_size = size - (ctx->buf_size - ctx->buf_ix);
764       ctx->overflow_buf = (char *)malloc (ctx->overflow_buf_size);
765       ctx->overflow_buf_ix = 0;
766       status = ijs_server_read_data (ctx, ctx->buf + ctx->buf_ix,
767 				     ctx->buf_size - ctx->buf_ix);
768       ctx->buf_ix = ctx->buf_size;
769       if (!status)
770 	{
771 	  status = ijs_server_read_data (ctx, ctx->overflow_buf,
772 					 ctx->overflow_buf_size);
773 	}
774     }
775   return ijs_server_ack (ctx);
776 }
777 
778 static int
ijs_server_proc_end_page(IjsServerCtx * ctx)779 ijs_server_proc_end_page (IjsServerCtx *ctx)
780 {
781 #ifdef VERBOSE
782   fprintf (stderr, "end page\n");
783 #endif
784   return ijs_server_ack (ctx);
785 }
786 
787 static int
ijs_server_proc_exit(IjsServerCtx * ctx)788 ijs_server_proc_exit (IjsServerCtx *ctx)
789 {
790   return 1;
791 }
792 
793 ijs_server_proc ijs_server_procs[] = {
794   ijs_server_proc_ack,
795   ijs_server_proc_nak,
796   ijs_server_proc_ping,
797   ijs_server_proc_pong,
798   ijs_server_proc_open,
799   ijs_server_proc_close,
800   ijs_server_proc_begin_job,
801   ijs_server_proc_end_job,
802   ijs_server_proc_cancel_job,
803   ijs_server_proc_query_status,
804   ijs_server_proc_list_params,
805   ijs_server_proc_enum_param,
806   ijs_server_proc_set_param,
807   ijs_server_proc_get_param,
808   ijs_server_proc_begin_page,
809   ijs_server_proc_send_data_block,
810   ijs_server_proc_end_page,
811   ijs_server_proc_exit
812 };
813 
814 int
ijs_server_iter(IjsServerCtx * ctx)815 ijs_server_iter (IjsServerCtx *ctx)
816 {
817   int cmd_num;
818   int status;
819 
820   status = ijs_recv_buf (&ctx->recv_chan);
821 
822   if (status < 0)
823     return status;
824 
825   cmd_num = ijs_get_int (ctx->recv_chan.buf);
826 #ifdef VERBOSE
827   fprintf (stderr, "command %d, %d bytes\n", cmd_num, ctx->recv_chan.buf_size);
828 #endif
829   if (cmd_num < 0 ||
830       cmd_num >= (int)sizeof(ijs_server_procs) / sizeof(ijs_server_procs[0]))
831     return -1;
832   return ijs_server_procs[cmd_num] (ctx);
833 }
834 
835 /**
836  * ijs_server_get_page_header: Get the page header.
837  * @ctx: The server context.
838  * @ph: Where to store the page header.
839  *
840  * Return value: 0 on success, 1 on normal exit, negative on error.
841  **/
842 int
ijs_server_get_page_header(IjsServerCtx * ctx,IjsPageHeader * ph)843 ijs_server_get_page_header (IjsServerCtx *ctx, IjsPageHeader *ph)
844 {
845   int status;
846 
847   ctx->ph = ph;
848   ctx->in_page = FALSE;
849 
850   do
851     {
852       status = ijs_server_iter (ctx);
853     }
854   while (status == 0 && !ctx->in_page);
855 
856   ctx->ph = NULL;
857   return status;
858 }
859 
860 /**
861  * ijs_server_get_data: Get data from client.
862  * @ctx: The server context.
863  * @buf: Buffer for data being read.
864  * @size: Size of buf.
865  *
866  * Gets data from client. Data is stored in @buf or the
867  * overflow_buf.
868  *
869  * Return value: Number of bytes read, -1 on end of page, or < 0 on
870  * error.
871  **/
872 int
ijs_server_get_data(IjsServerCtx * ctx,char * buf,int size)873 ijs_server_get_data (IjsServerCtx *ctx, char *buf, int size)
874 {
875   int buf_ix = 0;
876   int status = 0;
877 
878 #ifdef VERBOSE
879   fprintf (stderr, "ijs_server_get_data %d\n", size);
880 #endif
881 
882   if (ctx->overflow_buf != NULL)
883     {
884       int n_bytes = ctx->overflow_buf_size - ctx->overflow_buf_ix;
885       if (n_bytes > size)
886 	n_bytes = size;
887       memcpy (buf, ctx->overflow_buf + ctx->overflow_buf_ix, n_bytes);
888       ctx->overflow_buf_ix += n_bytes;
889       buf_ix = n_bytes;
890       if (ctx->overflow_buf_ix == ctx->overflow_buf_size)
891 	{
892 	  free (ctx->overflow_buf);
893 	  ctx->overflow_buf = NULL;
894 	  ctx->overflow_buf_size = 0;
895 	  ctx->overflow_buf_ix = 0;
896 	}
897     }
898   ctx->buf = buf;
899   ctx->buf_size = size;
900   ctx->buf_ix = buf_ix;
901   while (!status && ctx->buf_ix < size)
902     {
903       status = ijs_server_iter (ctx);
904     }
905   ctx->buf = NULL;
906   return status;
907 }
908