2434609144682bb6f02378c7f0215b92cbfe9334
[apps/core/preloaded/print-service.git] / src / pt_service.c
1 /*
2 *  Printservice
3 *
4 * Copyright 2012  Samsung Electronics Co., Ltd
5
6 * Licensed under the Flora License, Version 1.1 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9
10 * http://floralicense.org/license/
11
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 */
19
20 #define _GNU_SOURCE
21 #include <string.h>
22 #include <math.h>
23 #include <cups/ipp.h>
24 #include <cups/ppd.h>
25 #include <glib/gprintf.h>
26 #include <vconf.h>
27 #include <vconf-keys.h>
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <linux/types.h>
31 #include <linux/netlink.h>
32 #include <errno.h>
33 #include <unistd.h>
34 #include <arpa/inet.h>
35 #include <netinet/in.h>
36 #include <sys/ioctl.h>
37 #include <app.h>
38
39 #include "pt_debug.h"
40 #include "pt_common.h"
41 #include "pt_ppd.h"
42 #include "pt_utils.h"
43
44
45 #define AVAHI_DAEMON "/usr/sbin/avahi-daemon"
46 #define CUPS_DAEMON  "/usr/sbin/cupsd"
47
48 #define AVAHI_TEMP                      "/opt/var/run/avahi-daemon/pid"
49 #define CLEAR_JOB_TEMP          "rm -rf /opt/var/spool/cups/* /opt/var/run/cups/*;killall cupsd"
50
51 //#define JOB_IN_PROGRESS "Spooling job, "
52 #define JOB_COMPLETE "Job completed."
53 //#define JOB_PAGE_PRINTED "Printed "
54
55 pt_info_t *g_pt_info = NULL;
56
57 static Eina_Bool __pt_parse_job_complete(const char *msg)
58 {
59         PRINT_SERVICE_FUNC_ENTER;
60         gboolean ret = EINA_FALSE;
61
62         if (strstr(msg, JOB_COMPLETE) != NULL) {
63                 ret = EINA_TRUE;
64         }
65
66         PRINT_SERVICE_FUNC_LEAVE;
67         return ret;
68 }
69
70 static Eina_Bool __pt_create_job_subscription(int jobid)
71 {
72         PRINT_SERVICE_FUNC_ENTER;
73         PT_RETV_IF(jobid <= 0 , EINA_FALSE, "Invalid jobid[%d]", jobid);
74
75         char uri[HTTP_MAX_URI];
76         const char *events[100];
77         int num_events;
78         Eina_List *cursor = NULL;
79         pt_printing_job_t *printing_job = NULL;
80
81         EINA_LIST_FOREACH(g_pt_info->printing_thd_list, cursor, printing_job) {
82                 if (printing_job->job->id == jobid) {
83                         PT_DEBUG("Found printing_job for job[%d]", jobid);
84                         break;
85                 }
86         }
87         PT_RETV_IF(printing_job == NULL || printing_job->job->id <= 0, EINA_FALSE
88                            , "No found printing_job for job[%d]", jobid);
89
90         http_t *http;
91         ipp_t  *request, *response;
92         ipp_attribute_t *attr;
93
94         http = httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption());
95         request = ippNewRequest(IPP_CREATE_JOB_SUBSCRIPTION);
96
97         httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, "localhost", 631, "/printers/%s", g_pt_info->active_printer->name);
98         PT_DEBUG("request print-uri: %s\n", uri);
99         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
100
101         PT_DEBUG("request requesting-user-name: %s\n", cupsUser());
102         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
103
104         events[0] = "all";
105         num_events = 1;
106         ippAddStrings(request, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD, "notify-events",
107                                   num_events, NULL, events);
108         ippAddString(request, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
109                                  "notify-pull-method", NULL, "ippget");
110
111         if ((response = cupsDoRequest(http, request, "/")) != NULL) {
112                 PT_DEBUG("request ok!");
113
114                 if ((attr = ippFindAttribute(response, "notify-subscription-id", IPP_TAG_INTEGER)) != NULL) {
115                         printing_job->job->subscription_id = ippGetInteger(attr,0);
116                         printing_job->job->sequence_num = 0;
117                         PT_DEBUG("Subscription id[%d]\n", printing_job->job->subscription_id);
118                 } else {
119                         /*
120                          * If the printer does not return a job-state attribute, it does not
121                          * conform to the IPP specification - break out immediately and fail
122                          * the job...
123                          */
124
125                         PT_DEBUG("No notify-subscription-id available from job-uri.\n");
126                 }
127
128                 ippDelete(response);
129         }
130         httpClose(http);
131
132         PRINT_SERVICE_FUNC_LEAVE;
133         return EINA_TRUE;
134 }
135
136 static Eina_Bool __pt_parse_page_progress_value(const char *msg, int *page, int *progress)
137 {
138         PRINT_SERVICE_FUNC_ENTER;
139         PT_RETV_IF(msg == NULL || page == NULL || progress == NULL, EINA_FALSE, "Invalid argument");
140
141         int ret = 0;
142         ret = sscanf(msg, "Printing page %d, %d%%", page, progress);
143         PT_RETV_IF(ret != 2, EINA_FALSE, "Failed to sscanf page, progress");
144         PT_DEBUG("Parsed page: %d, progress:  %d", *page, *progress);
145
146         PRINT_SERVICE_FUNC_LEAVE;
147         return EINA_TRUE;
148 }
149
150 /*
151 * launch daemon, such as cups and avahi
152 */
153 static Eina_Bool __pt_get_cupsd_pid(void *data)
154 {
155         PRINT_SERVICE_FUNC_ENTER;
156         FILE *fd = NULL;
157         char *ret = NULL;
158         char pid_str[9] = {0,};
159         int pid = 0;
160
161         fd = fopen("/opt/var/run/cups/cupsd.pid", "r");
162         if (fd != NULL) {
163                 ret = fgets(pid_str, 8, fd);
164                 if (ret != NULL) {
165                         PT_DEBUG("ret : %s", ret);
166                         pid = atoi(pid_str);
167                         PT_DEBUG("CUPS process is %d", pid);
168                 }
169                 fclose(fd);
170         }
171         g_pt_info->cups_pid = pid;
172         PRINT_SERVICE_FUNC_LEAVE;
173         return ECORE_CALLBACK_CANCEL;
174 }
175
176 static void __pt_launch_daemon(void)
177 {
178         PRINT_SERVICE_FUNC_ENTER;
179
180         int pid = 0;
181         int pid2 = 0;
182
183         char *cups_argv[2];
184         char *avahi_argv[6];
185
186         cups_argv[0] = "cupsd";
187         cups_argv[1] = NULL;
188
189         avahi_argv[0] = "avahi-daemon";
190         avahi_argv[1] = "-s";
191         avahi_argv[2] = "--no-drop-root";
192         avahi_argv[3] = "--no-chroot";
193         avahi_argv[4] = "--debug";
194         avahi_argv[5] = NULL;
195
196         pt_utils_remove_files_in("/opt/var/spool/cups");
197         pt_utils_remove_files_in("/opt/var/run/cups");
198
199         /*
200         when temp file content is NULL
201         avahi daemon will not start
202         delete this file can avoid this issue
203         */
204         if (access(AVAHI_TEMP, F_OK) ==0) {
205                 if (unlink(AVAHI_TEMP) != 0) {
206                         PT_DEBUG("DEL_AVAHI_TEMP is failed(%d)", errno);
207                 }
208         }
209
210         pid = vfork();
211         if (0 == pid) {
212                 PT_DEBUG("I'm avahi process %d", getpid());
213                 if (-1 == execv(AVAHI_DAEMON, avahi_argv)) {
214                         PT_DEBUG("AVAHI failed");
215                 }
216         } else {
217                 PT_DEBUG("avahi process is %d", pid);
218                 g_pt_info->avahi_pid = pid;
219
220                 pid2 = vfork();
221                 if (0 == pid2) {
222                         PT_DEBUG("I'm CUPS process %d", getpid());
223                         if (-1 == execv(CUPS_DAEMON, cups_argv)) {
224                                 PT_DEBUG("exec failed");
225                         }
226                 } else {
227                         ecore_timer_add(2.0, (Ecore_Task_Cb)__pt_get_cupsd_pid, NULL);
228                         PT_DEBUG("CUPS parent process is %d", pid2);
229                 }
230         }
231         PRINT_SERVICE_FUNC_LEAVE;
232 }
233
234 static gboolean __pt_process_hotplug_event(GIOChannel *source, GIOCondition condition, gpointer data)
235 {
236         pt_info_t *info = (pt_info_t *)data;
237         int read_len = -1;
238         char buf[MAX_MSG_LEN] = {0,};
239
240         memset(buf, '\0', MAX_MSG_LEN);
241         read_len = recv(info->ueventsocket, buf, sizeof(buf)-1, 0);
242
243         if (read_len == -1) {
244                 return FALSE;
245         }
246
247         if (strstr(buf, "/usb/lp0")) {
248                 pt_event_e event;
249                 if (strstr(buf, "add@")) {
250                         event = PT_EVENT_USB_PRINTER_ONLINE;
251                 } else if (strstr(buf, "remove@")) {
252                         event = PT_EVENT_USB_PRINTER_OFFLINE;
253                 } else {
254                         PT_DEBUG("Unknown usb event!!! So just think offline");
255                         event = PT_EVENT_USB_PRINTER_OFFLINE;
256                 }
257
258                 if (info->evt_cb != NULL) {
259                         info->evt_cb(event, info->user_data, NULL);
260                 } else {
261                         PT_DEBUG("Failed to send hotplug event because of info->evt_cb is NULL");
262                         return FALSE;
263                 }
264         } else {
265                 return FALSE;
266         }
267
268         return TRUE;
269 }
270
271 static gboolean __pt_register_hotplug_event(void *data)
272 {
273         pt_info_t *info = (pt_info_t *)data;
274         PT_RETV_IF(info == NULL, FALSE, "Invalid argument");
275
276         int ret = -1;
277         int ueventsocket = -1;
278         GIOChannel *gio = NULL;
279         int g_source_id = -1;
280
281         ueventsocket = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
282         PT_RETV_IF(ueventsocket == -1 , FALSE, "create uevent socket failed. errno=%d", errno);
283
284         struct sockaddr_nl addr;
285         bzero(&addr, sizeof(struct sockaddr_nl));
286         addr.nl_family = AF_NETLINK;
287         addr.nl_groups = 1;
288         addr.nl_pid = getpid();
289
290         ret = bind(ueventsocket, (struct sockaddr *)&addr, sizeof(struct sockaddr_nl));
291         if (-1 == ret) {
292                 PT_DEBUG("bind uevent socket failed. errno=%d", errno);
293                 close(ueventsocket);
294                 return FALSE;
295         }
296
297         const int buffersize = 1024;
298         ret = setsockopt(ueventsocket, SOL_SOCKET, SO_RCVBUF, &buffersize, sizeof(buffersize));
299         if (-1 == ret) {
300                 PT_DEBUG("set uevent socket option failed. errno=%d", errno);
301                 close(ueventsocket);
302                 return FALSE;
303         }
304
305         info->ueventsocket = ueventsocket;
306         gio = g_io_channel_unix_new(ueventsocket);
307         g_source_id = g_io_add_watch(gio, G_IO_IN | G_IO_ERR | G_IO_HUP, (GIOFunc)__pt_process_hotplug_event, info);
308         g_io_channel_unref(gio);
309         PT_DEBUG("Socket is successfully registered to g_main_loop.\n");
310
311         return TRUE;
312 }
313
314 static void printing_thread_notify_cb(void *data, Ecore_Thread *thread, void *msg_data)
315 {
316         PRINT_SERVICE_FUNC_ENTER;
317
318         PT_DEBUG("Thread is sent msg successfully.");
319
320         ipp_t *response;
321         ipp_attribute_t *attr;
322         pt_progress_info_t progress_info = {0,};
323         pt_printing_job_t *printing_job = NULL;
324
325         if (data != NULL) {
326                 printing_job = (pt_printing_job_t *)data;
327         } else {
328                 PT_DEBUG("data is NULL.");
329                 // FIXME - no need to call evt_cb?
330                 return;
331         }
332
333         if (msg_data != NULL) {
334                 response = (ipp_t *)msg_data;
335         } else {
336                 PT_DEBUG("msg_data is NULL.");
337                 // FIXME - no need to call evt_cb?
338                 return;
339         }
340
341         PT_DEBUG("Received notification of JOB ID %d", printing_job->job->id);
342         progress_info.job_id = printing_job->job->id;
343         printing_job->page_printed = 0;
344
345         // Get progress value by parsing notify-text
346         if ((attr = ippFindAttribute(response, "notify-text", IPP_TAG_TEXT)) != NULL) {
347                 int progress;
348                 Eina_Bool job_completed;
349                 Eina_Bool ret;
350
351                 PT_DEBUG("notify-text : %s", ippGetString(attr, 0, NULL));
352
353                 ret = __pt_parse_page_progress_value(ippGetString(attr, 0, NULL),
354                                                                                          &printing_job->page_printed,
355                                                                                          &progress);
356
357                 if ((progress > 0) && (NULL != g_pt_info->evt_cb)) {
358                         progress_info.progress = progress;
359                         progress_info.page_printed = printing_job->page_printed;
360                         g_pt_info->evt_cb(PT_EVENT_JOB_PROGRESS, g_pt_info->user_data, &progress_info);
361                 }
362
363                 job_completed = __pt_parse_job_complete(ippGetString(attr, 0, NULL));
364                 if (job_completed == EINA_TRUE) {
365                         PT_DEBUG("Job completed. Stop timer callback to get notification");
366                         printing_job->printing_state = PT_PRINT_END;
367                         if (NULL != g_pt_info->evt_cb) {
368                                 g_pt_info->evt_cb(PT_EVENT_JOB_COMPLETED, g_pt_info->user_data, &progress_info);
369                         }
370                 }
371
372                 while (attr) {
373                         attr = ippFindNextAttribute(response, "notify-text", IPP_TAG_TEXT);
374                         if (attr) {
375                                 PT_DEBUG("notify-text : %s", ippGetString(attr, 0, NULL));
376                                 ret = __pt_parse_page_progress_value(ippGetString(attr, 0, NULL),
377                                                                                                          &printing_job->page_printed,
378                                                                                                          &progress);
379
380                                 if ((progress > 0) && (NULL != g_pt_info->evt_cb)) {
381                                         progress_info.progress = progress;
382                                         progress_info.page_printed = printing_job->page_printed;
383                                         g_pt_info->evt_cb(PT_EVENT_JOB_PROGRESS, g_pt_info->user_data, &progress_info);
384                                 }
385
386                                 job_completed = __pt_parse_job_complete(ippGetString(attr, 0, NULL));
387                                 if (job_completed == EINA_TRUE) {
388                                         PT_DEBUG("Job completed. Stop timer callback to get notification");
389                                         printing_job->printing_state = PT_PRINT_END;
390                                         if (NULL != g_pt_info->evt_cb) {
391                                                 g_pt_info->evt_cb(PT_EVENT_JOB_COMPLETED, g_pt_info->user_data, &progress_info);
392                                         }
393                                 }
394                         }
395                 }
396
397                 for (attr = ippFindAttribute(response, "notify-sequence-number", IPP_TAG_INTEGER);
398                                 attr;
399                                 attr = ippFindNextAttribute(response, "notify-sequence-number", IPP_TAG_INTEGER))
400                         if (ippGetInteger(attr, 0) > printing_job->job->sequence_num) {
401                                 printing_job->job->sequence_num = ippGetInteger(attr, 0);
402                         }
403
404                 ippDelete(response);
405         }
406
407         PRINT_SERVICE_FUNC_LEAVE;
408 }
409
410 static void printing_thread_end_cb(void *data, Ecore_Thread *thread)
411 {
412         PRINT_SERVICE_FUNC_ENTER;
413         PT_RET_IF(data == NULL || thread == NULL, "Invalid argument");
414         PT_DEBUG("Thread is completed successfully.");
415
416         pt_printing_job_t *printing_job = (pt_printing_job_t *)data;
417
418         Eina_List *printing_thd_list = NULL;
419         pt_printing_job_t *printing_job_item = NULL;
420         EINA_LIST_FOREACH(g_pt_info->printing_thd_list, printing_thd_list, printing_job_item) {
421                 PT_RET_IF(printing_job_item->job == NULL, "printing_job_item->job is NULL");
422                 if (printing_job_item->job->id == printing_job->job->id) {
423                         PT_DEBUG("Found printing_thd_list for job[%d]", printing_job_item->job->id);
424                         break;
425                 }
426         }
427
428         PT_RET_IF(printing_job == NULL || printing_job->job->id <= 0
429                           , "No found printing_job for job[%d]", printing_job->job->id);
430
431         g_pt_info->printing_thd_list = eina_list_remove_list(g_pt_info->printing_thd_list, printing_thd_list);
432
433         PT_IF_FREE_MEM(printing_job->job);
434         PT_IF_FREE_MEM(printing_job);
435
436         if (eina_list_count(g_pt_info->printing_thd_list) == 0) {
437                 if (g_pt_info->evt_cb != NULL) {
438                         g_pt_info->evt_cb(PT_EVENT_ALL_THREAD_COMPLETED, g_pt_info->user_data, NULL);
439                 }
440         }
441
442         PRINT_SERVICE_FUNC_LEAVE;
443 }
444
445 static void printing_thread_cancel_cb(void *data, Ecore_Thread *thread)
446 {
447         PRINT_SERVICE_FUNC_ENTER;
448         PT_RET_IF(data == NULL || thread == NULL, "Invalid argument");
449         PT_DEBUG("Thread is canceled successfully.");
450
451         pt_printing_job_t *printing_job = (pt_printing_job_t *)data;
452
453         Eina_List *printing_thd_list = NULL;
454         pt_printing_job_t *printing_job_item = NULL;
455         EINA_LIST_FOREACH(g_pt_info->printing_thd_list, printing_thd_list, printing_job_item) {
456                 PT_RET_IF(printing_job_item->job == NULL, "printing_job_item->job is NULL");
457                 if (printing_job_item->job->id == printing_job->job->id) {
458                         PT_DEBUG("Found printing_thd_list for job[%d]", printing_job_item->job->id);
459                         break;
460                 }
461         }
462         PT_RET_IF(printing_job == NULL || printing_job->job->id <= 0
463                           , "No found printing_job for job[%d]", printing_job->job->id);
464
465         g_pt_info->printing_thd_list = eina_list_remove_list(g_pt_info->printing_thd_list, printing_thd_list);
466
467         PT_IF_FREE_MEM(printing_job->job);
468         PT_IF_FREE_MEM(printing_job);
469
470         if (eina_list_count(g_pt_info->printing_thd_list) == 0) {
471                 if (g_pt_info->evt_cb != NULL) {
472                         g_pt_info->evt_cb(PT_EVENT_ALL_THREAD_COMPLETED, g_pt_info->user_data, NULL);
473                 }
474         }
475
476         PRINT_SERVICE_FUNC_LEAVE;
477 }
478
479 static void printing_thread(void *data, Ecore_Thread *thread)
480 {
481         PRINT_SERVICE_FUNC_ENTER;
482         pt_printing_job_t *printing_job = (pt_printing_job_t *)data;
483         //int *jobid = (int *)data;
484         char uri[HTTP_MAX_URI] = {0,};
485         http_t *http;
486         ipp_t  *request, *response;
487         pt_progress_info_t progress_info = {0,};
488
489         PT_DEBUG("print_file %s",printing_job->files[0]);
490
491         /*start printing file*/
492         printing_job->job->id = cupsPrintFiles(g_pt_info->active_printer->name, printing_job->num_files, printing_job->files, NULL,
493                                                                                    g_pt_info->num_options, g_pt_info->job_options);
494
495         cups_option_t *option = g_pt_info->job_options;
496         int i;
497         PT_DEBUG("++++++++++++ PRINTING ++++++++++++\n");
498         for (i=0; i < g_pt_info->num_options; i++) {
499                 PT_DEBUG("%d. %s=%s", i, option[i].name, option[i].value);
500         }
501
502         PT_RET_IF(printing_job->job->id <= 0, "print file failed, description:%s", cupsLastErrorString());
503
504         if (NULL != g_pt_info->evt_cb) {
505                 progress_info.job_id = printing_job->job->id;
506                 g_pt_info->evt_cb(PT_EVENT_JOB_STARTED, g_pt_info->user_data, &progress_info);
507         }
508
509         __pt_create_job_subscription(printing_job->job->id);
510         //__pt_register_job_progress(printing_job);
511
512         while (printing_job->printing_state == PT_PRINT_IN_PROGRESS || printing_job->printing_state == PT_PRINT_PENDING) {
513                 if (printing_job->printing_state == PT_PRINT_IN_PROGRESS) {
514                         http = httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption());
515                         request = ippNewRequest(IPP_GET_NOTIFICATIONS);
516
517                         httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, "localhost", 631, "/jobs/%d", printing_job->job->id);
518                         PT_DEBUG("request job-uri: %s\n", uri);
519                         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
520
521                         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
522                                                  "requesting-user-name", NULL, cupsUser());
523
524                         ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
525                                                   "notify-subscription-ids", printing_job->job->subscription_id);
526                         if (printing_job->job->sequence_num)
527                                 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
528                                                           "notify-sequence-numbers", printing_job->job->sequence_num + 1);
529
530                         if ((response = cupsDoRequest(http, request, "/")) != NULL) {
531                                 PT_DEBUG("request ok!");
532
533                                 if (ecore_thread_feedback(thread, (const void *)response) == EINA_FALSE) {
534                                         PT_DEBUG("Failed to send data to main loop");
535                                         ippDelete(response);
536                                 }
537                         }
538                         httpClose(http);
539                 }
540
541                 sleep(3);
542         }
543
544         PRINT_SERVICE_FUNC_LEAVE;
545 }
546
547 /**
548  *      This API let the app start the print job on the active printer, if not find active printer, will use default printer
549  *      now only support image and txt file,
550  *      will support pdf,office,eml,html in the future
551  *      @return   If success, return PT_ERR_NONE, else return the other error code as defined in pt_err_t
552  *      @param[in] files is the print files path address
553  *      @param[in] num is the print files number
554  */
555 int pt_start_print(const char **files, int num)
556 {
557         PRINT_SERVICE_FUNC_ENTER;
558         PT_RETV_IF(g_pt_info == NULL || g_pt_info->active_printer == NULL
559                            , PT_ERR_INVALID_PARAM, "Invalid argument");
560
561         PT_RETV_IF(files == NULL || num <=0, PT_ERR_INVALID_PARAM
562                            , "Params are not valid (num=%d)", num);
563
564         /* Check that connection is available */
565         int conn_type = 0;
566         int ret = 0;
567
568         pt_save_user_choice();
569
570         ret = pt_get_connection_status(&conn_type);
571         if (strcasestr(g_pt_info->active_printer->address,"usb://") != NULL) {
572                 if ((conn_type&PT_CONNECTION_USB) == 0) {
573                         PT_ERROR("USB printer is selected. But, USB is not available.");
574                         PRINT_SERVICE_FUNC_LEAVE;
575                         return PT_ERR_NOT_USB_ACCESS;
576                 }
577         } else {
578                 if ((conn_type&PT_CONNECTION_WIFI) == 0 &&
579                                 (conn_type&PT_CONNECTION_WIFI_DIRECT) == 0) {
580                         PT_ERROR("Network printer is selected. But, Network is not available.");
581                         PRINT_SERVICE_FUNC_LEAVE;
582                         return PT_ERR_NOT_NETWORK_ACCESS;
583                 }
584         }
585
586         /*get print options*/
587         char *instance = NULL;           /* Instance name */
588         cups_dest_t *dest = NULL;        /* Current destination */
589
590         if ((instance = strrchr(g_pt_info->active_printer->name, '/')) != NULL) {
591                 *instance++ = '\0';
592         }
593
594         dest = cupsGetNamedDest(CUPS_HTTP_DEFAULT,
595                                                         g_pt_info->active_printer->name,
596                                                         instance);
597         PT_RETV_IF(dest == NULL, PT_ERR_UNKNOWN, "get options failed");
598
599         pt_printing_job_t *printing_job = malloc(sizeof(pt_printing_job_t));
600         PT_RETV_IF(printing_job == NULL, PT_ERR_NO_MEMORY, "Failed to allocate memory for printing_job");
601
602         memset(printing_job, 0, sizeof(pt_printing_job_t));
603
604         printing_job->job = (pt_job_info_t *)calloc(1, sizeof(pt_job_info_t));
605         if (NULL == printing_job->job) {
606                 PT_IF_FREE_MEM(printing_job);
607                 PRINT_SERVICE_FUNC_LEAVE;
608                 return PT_ERR_NO_MEMORY;
609         }
610         memset(printing_job->job, 0, sizeof(pt_job_info_t));
611
612         printing_job->printing_state = PT_PRINT_IN_PROGRESS;
613         printing_job->files = files;
614         printing_job->num_files = num;
615         printing_job->printing_thd_hdl = ecore_thread_feedback_run(
616                                                                                  printing_thread,
617                                                                                  printing_thread_notify_cb,
618                                                                                  printing_thread_end_cb,
619                                                                                  printing_thread_cancel_cb,
620                                                                                  printing_job,
621                                                                                  EINA_FALSE);
622
623         g_pt_info->printing_thd_list = eina_list_append(g_pt_info->printing_thd_list, printing_job);
624         if (eina_error_get()) {
625                 PT_DEBUG("Failed to add eina_list for printing_thd_list");
626
627                 if (ecore_thread_cancel(printing_job->printing_thd_hdl) == EINA_FALSE) {
628                         PT_DEBUG("Canceling of printing_thd_hdl[%p] is pended", printing_job->printing_thd_hdl);
629                 }
630
631                 PT_IF_FREE_MEM(printing_job->job);
632                 PT_IF_FREE_MEM(printing_job);
633                 PRINT_SERVICE_FUNC_LEAVE;
634                 return PT_ERR_NO_MEMORY;
635         }
636
637         PRINT_SERVICE_FUNC_LEAVE;
638         return PT_ERR_NONE;
639 }
640
641 /**
642  *      This API let the app cancel the print job by jobid
643  *      @return   If success, return PT_ERR_NONE, else return the other error code as defined in pt_err_t
644  *      @param[in] jobid the job id
645  */
646 int pt_cancel_print(int jobid)
647 {
648         PRINT_SERVICE_FUNC_ENTER;
649         PT_RETV_IF(g_pt_info == NULL || g_pt_info->active_printer == NULL
650                            , PT_ERR_INVALID_PARAM, "Invalid argument");
651         PT_RETV_IF(jobid <= 0, PT_ERR_INVALID_PARAM, "Invalid argument");
652
653         Eina_List *cursor = NULL;
654         pt_printing_job_t *printing_job = NULL;
655         EINA_LIST_FOREACH(g_pt_info->printing_thd_list, cursor, printing_job) {
656                 if (printing_job->job->id == jobid) {
657                         PT_DEBUG("Found printing_thd_list for job[%d]", jobid);
658                         printing_job->printing_state = PT_PRINT_CANCEL;
659                         break;
660                 }
661         }
662         PT_RETV_IF(printing_job == NULL || printing_job->job->id <= 0
663                            , PT_ERR_INVALID_PARAM, "No found printing_job for job[%d]", jobid);
664
665         if (printing_job->job_noti_timer != NULL) {
666                 ecore_timer_del(printing_job->job_noti_timer);
667                 printing_job->job_noti_timer = NULL;
668         }
669
670         int ret = -1;
671         /*cancel printing file*/
672         ret = cupsCancelJob(g_pt_info->active_printer->name, jobid);
673         PT_RETV_IF(ret == 0, PT_ERR_UNKNOWN, "cancel job failed, description:%s", cupsLastErrorString());
674
675         if (ecore_thread_cancel(printing_job->printing_thd_hdl) == EINA_FALSE) {
676                 PT_DEBUG("Canceling of printing_thd_hdl[%p] is pended", printing_job->printing_thd_hdl);
677         }
678
679         PRINT_SERVICE_FUNC_LEAVE;
680         return PT_ERR_NONE;
681 }
682
683 /**
684  *      This API let the app initialize the environment(eg.ip address) by type
685  *      @return   If success, return PT_ERR_NONE, else return the other error code as defined in pt_err_t
686  *      @param[in] type connection type enumerated in pt_connection_type
687  */
688 int pt_init(pt_event_cb evt_cb, void *user_data)
689 {
690         PRINT_SERVICE_FUNC_ENTER;
691         PT_RETV_IF(g_pt_info != NULL, PT_ERR_NONE, "Initialized before");
692         PT_RETV_IF(evt_cb == NULL, PT_ERR_INVALID_USER_CB, "evt_cb is NULL");
693
694         g_pt_info = (pt_info_t *)calloc(1, sizeof(pt_info_t));
695         PT_RETV_IF(g_pt_info == NULL, PT_ERR_NO_MEMORY, "Failed to malloc");
696
697         g_pt_info->evt_cb = evt_cb;
698         g_pt_info->user_data = user_data;
699
700         __pt_launch_daemon();
701
702         g_pt_info->active_printer = (pt_printer_mgr_t *)calloc(1, sizeof(pt_printer_mgr_t));
703         PT_RETV_IF(g_pt_info->active_printer == NULL, PT_ERR_NO_MEMORY, "Failed to malloc");
704
705         g_pt_info->search = (pt_search_data_t *)calloc(1, sizeof(pt_search_data_t));
706         PT_RETV_IF(g_pt_info->search == NULL, PT_ERR_NO_MEMORY, "Failed to malloc");
707
708         g_pt_info->search->response_data.printerlist = NULL ;
709         g_pt_info->search->is_searching = 0 ;
710         g_pt_info->ueventsocket = -1;
711
712         __pt_register_hotplug_event(g_pt_info);
713
714         PRINT_SERVICE_FUNC_LEAVE;
715         return PT_ERR_NONE;
716 }
717
718 /**
719  *      This API let the app finalize the environment
720  *      @return   If success, return PT_ERR_NONE, else return the other error code as defined in pt_err_t
721  */
722 int pt_deinit(void)
723 {
724         PRINT_SERVICE_FUNC_ENTER;
725
726         if (g_pt_info) {
727                 if (g_pt_info->printing_thd_list) {
728                         pt_utils_free_printing_thd_list(g_pt_info->printing_thd_list);
729                         g_pt_info->printing_thd_list = NULL;
730                 }
731
732                 g_pt_info->ueventsocket = -1;
733
734                 g_pt_info->evt_cb = NULL;
735                 g_pt_info->user_data = NULL;
736
737                 kill(g_pt_info->cups_pid, SIGTERM);
738                 g_pt_info->cups_pid = -1;
739                 kill(g_pt_info->avahi_pid, SIGTERM);
740                 g_pt_info->avahi_pid = -1;
741
742         }
743
744         PRINT_SERVICE_FUNC_LEAVE;
745         return PT_ERR_NONE;
746 }
747
748 /**
749  *      This API let the app get the connect status
750  *      @return   If success, return PT_ERR_CONNECTION_USB_ACCESS or PT_ERR_CONNECTION_WIFI_ACCESS,
751  *      else return the other error code as defined in pt_err_t
752  */
753 int pt_get_connection_status(int *net_type)
754 {
755         PRINT_SERVICE_FUNC_ENTER;
756         int status = 0;
757         int connection_type = 0;
758         int ret = 0;
759
760         ret = vconf_get_int(VCONFKEY_SYSMAN_USB_HOST_STATUS, &status);
761         PT_RETV_IF(ret != 0, PT_ERR_FAIL, "Critical: Get VCONF usb status failed: %d", status);
762
763         PT_DEBUG("usb status: %d", status);
764
765         if (status == VCONFKEY_SYSMAN_USB_HOST_CONNECTED) {
766                 connection_type |= PT_CONNECTION_USB;
767         }
768
769         ret = vconf_get_int(VCONFKEY_WIFI_STATE, &status);
770         PT_RETV_IF(ret != 0,    PT_ERR_FAIL, "Critical: Get VCONF wifi status failed: %d", status);
771
772         PT_DEBUG("wifi status: %d", status);
773
774         if (status >= VCONFKEY_WIFI_CONNECTED) {
775                 connection_type |= PT_CONNECTION_WIFI;
776         }
777
778         ret = vconf_get_int(VCONFKEY_WIFI_DIRECT_STATE, &status);
779         PT_RETV_IF(ret != 0, PT_ERR_FAIL, "Critical: Get VCONF wifi-direct status failed: %d", status);
780
781         PT_DEBUG("wifi-direct status: %d", status);
782
783         if (status >= VCONFKEY_WIFI_DIRECT_CONNECTED) {
784                 connection_type |= PT_CONNECTION_WIFI_DIRECT;
785         }
786
787         *net_type = connection_type;
788
789         PRINT_SERVICE_FUNC_LEAVE;
790         return PT_ERR_NONE;
791 }
792
793 /**
794  *      This API let the app get the job status
795  *      @return   return job status
796  *      @param[in] job id
797  */
798 pt_job_state_e pt_get_current_job_status(int jobid)
799 {
800         PRINT_SERVICE_FUNC_ENTER;
801         PT_RETV_IF(g_pt_info == NULL || g_pt_info->active_printer == NULL, PT_JOB_ERROR, "Invalid g_pt_info");
802         PT_RETV_IF(jobid <= 0, PT_JOB_ERROR, "Invalid argument");
803
804         pt_event_e event;
805         pt_job_state_e ret                      = PT_JOB_ERROR;
806         pt_progress_info_t progress_info = {0,};
807         Eina_List *cursor                               = NULL;
808         pt_printing_job_t *printing_job         = NULL;
809
810         EINA_LIST_FOREACH(g_pt_info->printing_thd_list, cursor, printing_job) {
811                 if (printing_job->job->id == jobid) {
812                         PT_DEBUG("Found printing_thd_list for job[%d]", jobid);
813                         break;
814                 }
815         }
816
817         PT_RETV_IF(printing_job == NULL || printing_job->job->id <= 0, PT_JOB_ERROR
818                            , "No found printing_job for job[%d]", jobid);
819
820         int num_job             = 0;
821         int i                           = 0;
822         cups_job_t *jb  = NULL;
823         num_job = cupsGetJobs(&jb, g_pt_info->active_printer->name, 0, CUPS_WHICHJOBS_ALL);
824
825         PT_DEBUG("print job id is %d, job num is %d", jobid, num_job);
826         for (i = 0; i < num_job; i++) {
827                 PT_DEBUG("job id is %d, job state is %d", jb[i].id, jb[i].state);
828                 if (jb[i].id == jobid) {
829                         ret = jb[i].state;
830                         break;
831                 }
832         }
833
834         if (ret == PT_JOB_COMPLETED) {
835                 event = PT_EVENT_JOB_COMPLETED;
836                 printing_job->printing_state = PT_PRINT_END;
837         } else if (ret == PT_JOB_ABORTED) {
838                 event = PT_EVENT_JOB_ABORTED;
839                 printing_job->printing_state = PT_PRINT_ABORT;
840         } else if (ret == PT_JOB_CANCELED) {
841                 event = PT_EVENT_JOB_CANCELED;
842                 printing_job->printing_state = PT_PRINT_CANCEL;
843         } else if (ret == PT_JOB_STOPPED) {
844                 event = PT_EVENT_JOB_STOPPED;
845                 printing_job->printing_state = PT_PRINT_STOP;
846         } else if (ret == PT_JOB_PROCESSING) {
847                 event = PT_EVENT_JOB_PROCESSING;
848                 printing_job->printing_state = PT_PRINT_IN_PROGRESS;
849         } else if (ret == PT_JOB_HELD) {
850                 event = PT_EVENT_JOB_HELD;
851                 printing_job->printing_state = PT_PRINT_HELD;
852         } else if (ret == PT_JOB_PENDING) {
853                 event = PT_EVENT_JOB_PENDING;
854                 printing_job->printing_state = PT_PRINT_PENDING;
855         } else {
856                 event = PT_EVENT_JOB_ERROR;
857         }
858
859         if (g_pt_info->evt_cb != NULL) {
860                 progress_info.job_id = jobid;
861                 g_pt_info->evt_cb(event, g_pt_info->user_data, &progress_info);
862         } else {
863                 PT_DEBUG("Failed to send job status to application because of g_pt_info->evt_cb is NULL");
864         }
865
866         PRINT_SERVICE_FUNC_LEAVE;
867         return ret;
868 }
869
870 /**
871  *      This API check the printer status
872  *      @return   If success, return PT_ERR_NONE, else return the other error code as defined in pt_err_t
873  */
874 pt_printer_state_e pt_check_printer_status(pt_printer_mgr_t *printer)
875 {
876         PRINT_SERVICE_FUNC_ENTER;
877         PT_RETV_IF(g_pt_info->search == NULL || printer == NULL, PT_PRINTER_OFFLINE, "Printer is offline");
878
879         // TODO: Need to replace new function without using avahi
880         // XXX FIXME XXX
881         if (g_pt_info->evt_cb != NULL) {
882                 g_pt_info->evt_cb(PT_EVENT_PRINTER_ONLINE, g_pt_info->user_data, NULL);
883         } else {
884                 PT_DEBUG("Failed to send printer status to application because of g_pt_info->evt_cb is NULL");
885         }
886
887         PRINT_SERVICE_FUNC_LEAVE;
888         return PT_PRINTER_IDLE;
889 }
890
891 //NOTICE : Use this function instead of system() which has security hole.
892 //if envp is NULL, execute execv. otherwise, execute execvp.
893 //Argument array should include NULL as last element
894 /*
895 int execute_filter(const char *filter, const char *argv[], char *const envp[]){
896
897         pid_t pid = 0;
898         int status = 0;
899
900         if ( filter == NULL || argv == NULL ){
901                 PT_DEBUG("argument is invalid");
902                 return -1;
903         }
904
905         pid = fork();
906
907         if (pid == -1){
908                 PT_DEBUG("Failed to fork");
909                 return -1;
910         }
911
912         if (pid == 0) {
913                 if(envp != NULL){
914                         if ( execve(filter, argv, envp) < 0 ){
915                                 PT_DEBUG("Can't execute(%d)",errno);
916                         }
917                 }
918                 else{
919                         if ( execv(filter, argv) < 0 ){
920                                 PT_DEBUG("Can't execute(%d)",errno);
921                         }
922                 }
923                 exit(127);
924         }
925
926         do {
927                 if (waitpid(pid, &status, 0) == -1) {
928                         if (errno != EINTR){
929                                 PT_DEBUG("Errno is not EINTR");
930                                 return -1;
931                         }
932                 }
933                 else {
934                         PT_DEBUG("Parent process is normally terminated");
935                         return status;
936                 }
937         } while (1);
938 }
939 */