1 /* librepo - A library providing (libcURL like) API to downloading repository
2  * Copyright (C) 2012  Tomas Mlcoch
3  *
4  * Licensed under the GNU Lesser General Public License Version 2.1
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 #include <Python.h>
22 #undef NDEBUG
23 #include <assert.h>
24 
25 #include "librepo/librepo.h"
26 
27 #include "exception-py.h"
28 #include "handle-py.h"
29 #include "packagetarget-py.h"
30 #include "result-py.h"
31 #include "typeconversion.h"
32 #include "packagedownloader-py.h"
33 #include "downloader-py.h"
34 
35 #include "globalstate-py.h"  // GIL Hack
36 
37 typedef struct {
38     PyObject_HEAD
39     LrHandle *handle;
40     /* Callbacks */
41     PyObject *progress_cb;
42     PyObject *progress_cb_data;
43     PyObject *fastestmirror_cb;
44     PyObject *fastestmirror_cb_data;
45     PyObject *hmf_cb;
46     /* GIL stuff */
47     // See: http://docs.python.org/2/c-api/init.html#releasing-the-gil-from-extension-code
48     PyThreadState **state;
49 } _HandleObject;
50 
51 LrHandle *
Handle_FromPyObject(PyObject * o)52 Handle_FromPyObject(PyObject *o)
53 {
54     if (!HandleObject_Check(o)) {
55         PyErr_SetString(PyExc_TypeError, "Expected a _librepo.Handle object.");
56         return NULL;
57     }
58     return ((_HandleObject *)o)->handle;
59 }
60 
61 void
Handle_SetThreadState(PyObject * o,PyThreadState ** state)62 Handle_SetThreadState(PyObject *o, PyThreadState **state)
63 {
64     _HandleObject *self = (_HandleObject *) o;
65     if (!self) return;
66     self->state = state;
67 }
68 
69 static int
check_HandleStatus(const _HandleObject * self)70 check_HandleStatus(const _HandleObject *self)
71 {
72     assert(self != NULL);
73     assert(HandleObject_Check(self));
74     if (self->handle == NULL) {
75         PyErr_SetString(LrErr_Exception, "No librepo handle");
76         return -1;
77     }
78     return 0;
79 }
80 
81 /* Callback stuff */
82 
83 static int
progress_callback(void * data,double total_to_download,double now_downloaded)84 progress_callback(void *data, double total_to_download, double now_downloaded)
85 {
86     int ret = LR_CB_OK; // Assume everything will be ok
87     _HandleObject *self;
88     PyObject *user_data, *result;
89 
90     self = (_HandleObject *)data;
91     if (!self->progress_cb)
92         return LR_CB_OK;
93 
94     if (self->progress_cb_data)
95         user_data = self->progress_cb_data;
96     else
97         user_data = Py_None;
98 
99     EndAllowThreads(self->state);
100     result = PyObject_CallFunction(self->progress_cb,
101                         "(Odd)", user_data, total_to_download, now_downloaded);
102 
103     if (!result) {
104         // Exception raised in callback leads to the abortion
105         // of whole downloading (it is considered fatal)
106         ret = LR_CB_ERROR;
107     } else {
108         if (result == Py_None) {
109             // Assume that None means that everything is ok
110             ret = LR_CB_OK;
111         } else if (PyLong_Check(result)) {
112             ret = (int) PyLong_AsLong(result);
113         } else {
114             // It's an error if result is None neither int
115             PyErr_SetString(PyExc_TypeError, "Progress callback must return integer number");
116             ret = LR_CB_ERROR;
117         }
118     }
119 
120     Py_XDECREF(result);
121     BeginAllowThreads(self->state);
122 
123     return ret;
124 }
125 
126 static void
fastestmirror_callback(void * data,LrFastestMirrorStages stage,void * ptr)127 fastestmirror_callback(void *data, LrFastestMirrorStages stage, void *ptr)
128 {
129     _HandleObject *self;
130     PyObject *user_data, *result, *pydata;
131 
132     self = (_HandleObject *)data;
133     if (!self->fastestmirror_cb)
134         return;
135 
136     if (self->fastestmirror_cb_data)
137         user_data = self->fastestmirror_cb_data;
138     else
139         user_data = Py_None;
140 
141     EndAllowThreads(self->state);
142 
143     if (!ptr) {
144         pydata = Py_None;
145     } else {
146         switch (stage) {
147         case LR_FMSTAGE_CACHELOADING:
148         case LR_FMSTAGE_CACHELOADINGSTATUS:
149         case LR_FMSTAGE_STATUS:
150             pydata = PyStringOrNone_FromString((char *) ptr);
151             break;
152         case LR_FMSTAGE_DETECTION:
153             pydata = PyLong_FromLong(*((long *) ptr));
154             break;
155         default:
156             pydata = Py_None;
157         }
158     }
159 
160     result = PyObject_CallFunction(self->fastestmirror_cb,
161                         "(OlO)", user_data, (long) stage, pydata);
162     Py_XDECREF(result);
163     BeginAllowThreads(self->state);
164 
165     if (pydata != Py_None)
166         Py_XDECREF(pydata);
167 
168     return;
169 }
170 
171 static int
hmf_callback(void * data,const char * msg,const char * url,const char * metadata)172 hmf_callback(void *data, const char *msg, const char *url, const char *metadata)
173 {
174     int ret = LR_CB_OK; // Assume everything will be ok
175     _HandleObject *self;
176     PyObject *user_data, *result, *py_msg, *py_url, *py_metadata;
177 
178     self = (_HandleObject *)data;
179     if (!self->hmf_cb)
180         return LR_CB_OK;
181 
182     if (self->progress_cb_data)
183         user_data = self->progress_cb_data;
184     else
185         user_data = Py_None;
186 
187     EndAllowThreads(self->state);
188 
189     py_msg = PyStringOrNone_FromString(msg);
190     py_url = PyStringOrNone_FromString(url);
191     py_metadata = PyStringOrNone_FromString(metadata);
192 
193     result = PyObject_CallFunction(self->hmf_cb,
194                         "(OOOO)", user_data, py_msg, py_url, py_metadata);
195 
196     Py_DECREF(py_msg);
197     Py_DECREF(py_url);
198     Py_DECREF(py_metadata);
199 
200     if (!result) {
201         // Exception raised in callback leads to the abortion
202         // of whole downloading (it is considered fatal)
203         ret = LR_CB_ERROR;
204     } else {
205         if (result == Py_None) {
206             // Assume that None means that everything is ok
207             ret = LR_CB_OK;
208         } else if (PyLong_Check(result)) {
209             ret = (int) PyLong_AsLong(result);
210         } else {
211             // It's an error if result is None neither int
212             PyErr_SetString(PyExc_TypeError, "HandleMirrorFailure callback must return integer number");
213             ret = LR_CB_ERROR;
214         }
215     }
216 
217     Py_XDECREF(result);
218     BeginAllowThreads(self->state);
219 
220     return ret;
221 }
222 
223 /* Function on the type */
224 
225 static PyObject *
handle_new(PyTypeObject * type,G_GNUC_UNUSED PyObject * args,G_GNUC_UNUSED PyObject * kwds)226 handle_new(PyTypeObject *type,
227            G_GNUC_UNUSED PyObject *args,
228            G_GNUC_UNUSED PyObject *kwds)
229 {
230     _HandleObject *self = (_HandleObject *)type->tp_alloc(type, 0);
231 
232     if (self) {
233         self->handle = NULL;
234         self->progress_cb = NULL;
235         self->progress_cb_data = NULL;
236         self->fastestmirror_cb = NULL;
237         self->fastestmirror_cb_data = NULL;
238         self->hmf_cb = NULL;
239         self->state = NULL;
240     }
241     return (PyObject *)self;
242 }
243 
244 static int
handle_init(_HandleObject * self,PyObject * args,PyObject * kwds)245 handle_init(_HandleObject *self, PyObject *args, PyObject *kwds)
246 {
247     char *kwlist[] = {NULL};
248 
249     if (!PyArg_ParseTupleAndKeywords(args, kwds, "|", kwlist))
250         return -1;
251 
252     self->handle = lr_handle_init();
253     if (self->handle == NULL) {
254         PyErr_SetString(LrErr_Exception, "Handle initialization failed");
255         return -1;
256     }
257 
258     return 0;
259 }
260 
261 static void
handle_dealloc(_HandleObject * o)262 handle_dealloc(_HandleObject *o)
263 {
264     if (o->handle)
265         lr_handle_free(o->handle);
266     Py_XDECREF(o->progress_cb);
267     Py_XDECREF(o->progress_cb_data);
268     Py_XDECREF(o->fastestmirror_cb);
269     Py_XDECREF(o->fastestmirror_cb_data);
270     Py_XDECREF(o->hmf_cb);
271     Py_TYPE(o)->tp_free(o);
272 }
273 
274 static PyObject *
py_setopt(_HandleObject * self,PyObject * args)275 py_setopt(_HandleObject *self, PyObject *args)
276 {
277     int option;
278     PyObject *obj;
279     gboolean res = TRUE;
280     GError *tmp_err = NULL;
281 
282     if (!PyArg_ParseTuple(args, "iO:py_setopt", &option, &obj))
283         return NULL;
284     if (check_HandleStatus(self))
285         return NULL;
286 
287     switch (option) {
288 
289     /*
290      * Options with string arguments (NULL is supported)
291      */
292     case LRO_MIRRORLIST:
293     case LRO_MIRRORLISTURL:
294     case LRO_METALINKURL:
295     case LRO_USERPWD:
296     case LRO_PROXY:
297     case LRO_PROXYUSERPWD:
298     case LRO_DESTDIR:
299     case LRO_USERAGENT:
300     case LRO_FASTESTMIRRORCACHE:
301     case LRO_GNUPGHOMEDIR:
302     case LRO_SSLCLIENTCERT:
303     case LRO_SSLCLIENTKEY:
304     case LRO_SSLCACERT:
305     case LRO_PROXY_SSLCLIENTCERT:
306     case LRO_PROXY_SSLCLIENTKEY:
307     case LRO_PROXY_SSLCACERT:
308     case LRO_CACHEDIR:
309     {
310         char *str = NULL, *alloced = NULL;
311 
312         if (PyUnicode_Check(obj)) {
313             PyObject *bytes = PyUnicode_AsUTF8String(obj);
314             if (!bytes) return NULL;
315             str = alloced = g_strdup(PyBytes_AsString(bytes));
316             Py_XDECREF(bytes);
317         } else if (PyBytes_Check(obj)) {
318             str = PyBytes_AsString(obj);
319         } else if (obj != Py_None) {
320             PyErr_SetString(PyExc_TypeError,
321                         "Only string or None is supported with this option");
322             return NULL;
323         }
324 
325         res = lr_handle_setopt(self->handle,
326                                &tmp_err,
327                                (LrHandleOption)option,
328                                str);
329         g_free(alloced);
330         break;
331     }
332 
333     /*
334      * Options with double arguments
335      */
336     case LRO_FASTESTMIRRORTIMEOUT:
337     {
338         double d;
339 
340         if (PyFloat_Check(obj))
341             d = PyFloat_AS_DOUBLE(obj);
342         else if (obj == Py_None) {
343             // None stands for default value
344             d = LRO_FASTESTMIRRORTIMEOUT_DEFAULT;
345         } else {
346             PyErr_SetString(PyExc_TypeError, "Only float or None is supported with this option");
347             return NULL;
348         }
349 
350         res = lr_handle_setopt(self->handle,
351                                &tmp_err,
352                                (LrHandleOption)option,
353                                d);
354         break;
355     }
356 
357     /*
358      * Options with long/int (boolean) arguments
359      */
360     case LRO_UPDATE:
361     case LRO_LOCAL:
362     case LRO_HTTPAUTH:
363     case LRO_PROXYAUTH:
364     case LRO_GPGCHECK:
365     case LRO_IGNOREMISSING:
366     case LRO_CHECKSUM:
367     case LRO_INTERRUPTIBLE:
368     case LRO_FETCHMIRRORS:
369     case LRO_FASTESTMIRROR:
370     case LRO_SSLVERIFYPEER:
371     case LRO_SSLVERIFYSTATUS:
372     case LRO_SSLVERIFYHOST:
373     case LRO_PROXY_SSLVERIFYPEER:
374     case LRO_PROXY_SSLVERIFYHOST:
375     case LRO_ADAPTIVEMIRRORSORTING:
376     case LRO_FTPUSEEPSV:
377     case LRO_PRESERVETIME:
378     case LRO_OFFLINE:
379     {
380         long d;
381 
382         // Default values for None attribute
383         if (obj == Py_None && (option == LRO_SSLVERIFYPEER || option == LRO_PROXY_SSLVERIFYPEER ||
384                                option == LRO_SSLVERIFYHOST || option == LRO_PROXY_SSLVERIFYHOST))
385         {
386             d = 1;
387         } else if (obj == Py_None && option == LRO_ADAPTIVEMIRRORSORTING) {
388             d = LRO_ADAPTIVEMIRRORSORTING_DEFAULT;
389         } else if (obj == Py_None && option == LRO_FTPUSEEPSV) {
390             d = LRO_FTPUSEEPSV_DEFAULT;
391         // end of default attributes
392         } else if (PyObject_IsTrue(obj) == 1)
393             d = 1;
394         else if (PyObject_IsTrue(obj) == 0)
395             d = 0;
396         else {
397             PyErr_SetString(PyExc_TypeError, "Only Int, Long or Bool are supported with this option");
398             return NULL;
399         }
400 
401         res = lr_handle_setopt(self->handle,
402                                &tmp_err,
403                                (LrHandleOption)option,
404                                d);
405         break;
406     }
407 
408     /*
409      * Options with long/int arguments
410      */
411     case LRO_PROXYTYPE:
412     case LRO_REPOTYPE:
413     case LRO_FASTESTMIRRORMAXAGE:
414     case LRO_LOWSPEEDTIME:
415     case LRO_LOWSPEEDLIMIT:
416     case LRO_IPRESOLVE:
417     case LRO_ALLOWEDMIRRORFAILURES:
418     {
419         int badarg = 0;
420         long d;
421 
422         if (PyLong_Check(obj))
423             d = PyLong_AsLong(obj);
424         else if (obj == Py_None) {
425             // None stands for default value
426             switch (option) {
427             case LRO_PROXYTYPE:
428                 d = LRO_PROXYTYPE_DEFAULT;
429                 break;
430             case LRO_LOWSPEEDTIME:
431                 d = LRO_LOWSPEEDTIME_DEFAULT;
432                 break;
433             case LRO_LOWSPEEDLIMIT:
434                 d = LRO_LOWSPEEDLIMIT_DEFAULT;
435                 break;
436             case LRO_FASTESTMIRRORMAXAGE:
437                 d = LRO_FASTESTMIRRORMAXAGE_DEFAULT;
438                 break;
439             case LRO_IPRESOLVE:
440                 d = LRO_IPRESOLVE_DEFAULT;
441                 break;
442             case LRO_ALLOWEDMIRRORFAILURES:
443                 d = LRO_ALLOWEDMIRRORFAILURES_DEFAULT;
444                 break;
445             default:
446                 badarg = 1;
447             }
448         } else
449             badarg = 1;
450 
451         if (badarg) {
452             PyErr_SetString(PyExc_TypeError, "Only Int/Long is supported with this option");
453             return NULL;
454         }
455 
456         res = lr_handle_setopt(self->handle,
457                                &tmp_err,
458                                (LrHandleOption)option,
459                                d);
460         break;
461     }
462 
463     /*
464      * Options with long/int/None arguments
465      */
466     case LRO_PROXYPORT:
467     case LRO_CONNECTTIMEOUT:
468     case LRO_MAXMIRRORTRIES:
469     case LRO_MAXPARALLELDOWNLOADS:
470     case LRO_MAXDOWNLOADSPERMIRROR:
471     case LRO_HTTPAUTHMETHODS:
472     case LRO_PROXYAUTHMETHODS:
473     {
474         long d;
475 
476         if (PyLong_Check(obj))
477             d = PyLong_AsLong(obj);
478         else if (obj == Py_None) {
479             /* Default options */
480             if (option == LRO_PROXYPORT)
481                 d = LRO_PROXYPORT_DEFAULT;
482             else if (option == LRO_MAXMIRRORTRIES)
483                 d = LRO_MAXMIRRORTRIES_DEFAULT;
484             else if (option == LRO_CONNECTTIMEOUT)
485                 d = LRO_CONNECTTIMEOUT_DEFAULT;
486             else if (option == LRO_MAXPARALLELDOWNLOADS)
487                 d = LRO_MAXPARALLELDOWNLOADS_DEFAULT;
488             else if (option == LRO_MAXDOWNLOADSPERMIRROR)
489                 d = LRO_MAXDOWNLOADSPERMIRROR_DEFAULT;
490             else if (option == LRO_HTTPAUTHMETHODS)
491                 d = LRO_HTTPAUTHMETHODS_DEFAULT;
492             else if (option == LRO_PROXYAUTHMETHODS)
493                 d = LRO_PROXYAUTHMETHODS_DEFAULT;
494             else
495                 assert(0);
496         } else {
497             PyErr_SetString(PyExc_TypeError, "Only Int/Long/None is supported with this option");
498             return NULL;
499         }
500 
501         res = lr_handle_setopt(self->handle,
502                                &tmp_err,
503                                (LrHandleOption)option,
504                                d);
505         break;
506     }
507 
508     /*
509      * Options with gint64/None arguments
510      */
511     case LRO_MAXSPEED:
512     {
513         gint64 d;
514 
515         if (PyLong_Check(obj))
516             d = (gint64) PyLong_AsLongLong(obj);
517         else if (obj == Py_None) {
518             /* Default options */
519             if (option == LRO_MAXSPEED)
520                 d = (gint64) LRO_MAXSPEED_DEFAULT;
521             else
522                 assert(0);
523         } else {
524             PyErr_SetString(PyExc_TypeError, "Only Int/Long/None is supported with this option");
525             return NULL;
526         }
527 
528         res = lr_handle_setopt(self->handle,
529                                &tmp_err,
530                                (LrHandleOption)option,
531                                d);
532         break;
533     }
534 
535     /*
536      * Options with array argument
537      */
538     case LRO_URLS:
539     case LRO_YUMDLIST:
540     case LRO_YUMBLIST:
541     case LRO_HTTPHEADER:
542     {
543         Py_ssize_t len = 0;
544 
545         if (!PyList_Check(obj) && obj != Py_None) {
546             PyErr_SetString(PyExc_TypeError, "Only List or None type is supported with this option");
547             return NULL;
548         }
549 
550         if (obj == Py_None) {
551             res = lr_handle_setopt(self->handle,
552                                    &tmp_err,
553                                    (LrHandleOption)option,
554                                    NULL);
555             break;
556         }
557 
558         len = PyList_Size(obj);
559         for (Py_ssize_t x = 0; x < len; x++) {
560             PyObject *item = PyList_GetItem(obj, x);
561             if (!PyBytes_Check(item) && !PyUnicode_Check(item) && item != Py_None) {
562                 PyErr_SetString(PyExc_TypeError, "Only strings or Nones are supported in list");
563                 return NULL;
564             }
565         }
566 
567         GStringChunk *chunk = g_string_chunk_new(0);
568         GPtrArray *ptrarray = g_ptr_array_sized_new(len + 1);
569         for (Py_ssize_t x = 0; x < len; x++) {
570             PyObject *item = PyList_GetItem(obj, x);
571 
572             if (PyUnicode_Check(item)) {
573                 PyObject *bytes = PyUnicode_AsUTF8String(item);
574                 if (!bytes) {
575                     g_ptr_array_free(ptrarray, TRUE);
576                     g_string_chunk_free(chunk);
577                     return NULL;
578                 }
579                 char *item_str = g_string_chunk_insert(chunk,
580                                                     PyBytes_AsString(bytes));
581                 Py_XDECREF(bytes);
582                 g_ptr_array_add(ptrarray, item_str);
583             }
584 
585             if (PyBytes_Check(item))
586                 g_ptr_array_add(ptrarray, PyBytes_AsString(item));
587         }
588         g_ptr_array_add(ptrarray, NULL);
589 
590         res = lr_handle_setopt(self->handle,
591                                &tmp_err,
592                                (LrHandleOption)option,
593                                ptrarray->pdata);
594         g_string_chunk_free(chunk);
595         g_ptr_array_free(ptrarray, TRUE);
596         break;
597     }
598 
599     case LRO_YUMSLIST:
600     case LRO_VARSUB: {
601         Py_ssize_t len = 0;
602         LrUrlVars *vars = NULL;
603 
604         if (!PyList_Check(obj) && obj != Py_None) {
605             PyErr_SetString(PyExc_TypeError, "Only List of tuples or None type is supported with this option");
606             return NULL;
607         }
608 
609         if (obj == Py_None) {
610             res = lr_handle_setopt(self->handle,
611                                    &tmp_err,
612                                    (LrHandleOption)option,
613                                    NULL);
614             break;
615         }
616 
617         // Check all list elements
618         len = PyList_Size(obj);
619         for (Py_ssize_t x = 0; x < len; x++) {
620             PyObject *item = PyList_GetItem(obj, x);
621             PyObject *tuple_item;
622 
623             if (!PyTuple_Check(item) || PyTuple_Size(item) != 2) {
624                 PyErr_SetString(PyExc_TypeError, "List elements has to be "
625                     "tuples with exactly 2 elements");
626                 return NULL;
627             }
628 
629             tuple_item = PyTuple_GetItem(item, 1);
630             if ((!PyBytes_Check(PyTuple_GetItem(item, 0))
631                 && !PyUnicode_Check(PyTuple_GetItem(item, 0))) ||
632                 (!PyBytes_Check(tuple_item)
633                  && !PyUnicode_Check(tuple_item)
634                  && tuple_item != Py_None))
635             {
636                 PyErr_SetString(PyExc_TypeError, "Bad list format");
637                 return NULL;
638             }
639         }
640 
641         GStringChunk *chunk = g_string_chunk_new(0);
642         for (Py_ssize_t x = 0; x < len; x++) {
643             PyObject *item = PyList_GetItem(obj, x);
644             PyObject *tuple_item;
645             char *var, *val;
646 
647             tuple_item = PyTuple_GetItem(item, 0);
648             if (PyBytes_Check(tuple_item)) {
649                 // PyBytes
650                 var = PyBytes_AsString(tuple_item);
651             } else {
652                 // PyUnicode
653                 PyObject *bytes = PyUnicode_AsUTF8String(tuple_item);
654                 if (!bytes) {
655                     lr_urlvars_free(vars);
656                     g_string_chunk_free(chunk);
657                     return NULL;
658                 }
659                 char *item_str = g_string_chunk_insert(chunk,
660                                                     PyBytes_AsString(bytes));
661                 Py_XDECREF(bytes);
662                 var = item_str;
663             }
664 
665             tuple_item = PyTuple_GetItem(item, 1);
666             if (tuple_item == Py_None) {
667                 // Py_None
668                 val = NULL;
669             } else if (PyBytes_Check(tuple_item)) {
670                 // PyBytes
671                 val = PyBytes_AsString(tuple_item);
672             } else {
673                 // PyUnicode
674                 PyObject *bytes = PyUnicode_AsUTF8String(tuple_item);
675                 if (!bytes) {
676                     lr_urlvars_free(vars);
677                     g_string_chunk_free(chunk);
678                     return NULL;
679                 }
680                 char *item_str = g_string_chunk_insert(chunk,
681                                                     PyBytes_AsString(bytes));
682                 Py_XDECREF(bytes);
683                 val = item_str;
684             }
685 
686             vars = lr_urlvars_set(vars, var, val);
687         }
688 
689         res = lr_handle_setopt(self->handle,
690                                &tmp_err,
691                                (LrHandleOption)option,
692                                vars);
693         g_string_chunk_free(chunk);
694         break;
695     }
696 
697     /*
698      * Options with callable arguments
699      */
700     case LRO_PROGRESSCB: {
701         if (!PyCallable_Check(obj) && obj != Py_None) {
702             PyErr_SetString(PyExc_TypeError, "Only callable argument or None is supported with this option");
703             return NULL;
704         }
705 
706         Py_XDECREF(self->progress_cb);
707         if (obj == Py_None) {
708             // None object
709             self->progress_cb = NULL;
710             res = lr_handle_setopt(self->handle,
711                                    &tmp_err,
712                                    (LrHandleOption)option,
713                                    NULL);
714             if (!res)
715                 RETURN_ERROR(&tmp_err, -1, NULL);
716         } else {
717             // New callback object
718             Py_XINCREF(obj);
719             self->progress_cb = obj;
720             res = lr_handle_setopt(self->handle,
721                                    &tmp_err,
722                                    (LrHandleOption)option,
723                                    progress_callback);
724             if (!res)
725                 RETURN_ERROR(&tmp_err, -1, NULL);
726             res = lr_handle_setopt(self->handle,
727                                    &tmp_err,
728                                    LRO_PROGRESSDATA,
729                                    self);
730         }
731         break;
732     }
733 
734     case LRO_FASTESTMIRRORCB: {
735         if (!PyCallable_Check(obj) && obj != Py_None) {
736             PyErr_SetString(PyExc_TypeError, "Only callable argument or None is supported with this option");
737             return NULL;
738         }
739 
740         Py_XDECREF(self->fastestmirror_cb);
741         if (obj == Py_None) {
742             // None object
743             self->fastestmirror_cb = NULL;
744             res = lr_handle_setopt(self->handle,
745                                    &tmp_err,
746                                    (LrHandleOption)option,
747                                    NULL);
748             if (!res)
749                 RETURN_ERROR(&tmp_err, -1, NULL);
750         } else {
751             // New callback object
752             Py_XINCREF(obj);
753             self->fastestmirror_cb = obj;
754             res = lr_handle_setopt(self->handle,
755                                    &tmp_err,
756                                    (LrHandleOption)option,
757                                    fastestmirror_callback);
758             if (!res)
759                 RETURN_ERROR(&tmp_err, -1, NULL);
760             res = lr_handle_setopt(self->handle,
761                                    &tmp_err,
762                                    LRO_FASTESTMIRRORDATA,
763                                    self);
764         }
765         break;
766     }
767 
768     case LRO_HMFCB: {
769         if (!PyCallable_Check(obj) && obj != Py_None) {
770             PyErr_SetString(PyExc_TypeError, "Only callable argument or None is supported with this option");
771             return NULL;
772         }
773 
774         Py_XDECREF(self->hmf_cb);
775         if (obj == Py_None) {
776             // None object
777             self->hmf_cb = NULL;
778             res = lr_handle_setopt(self->handle,
779                                    &tmp_err,
780                                    (LrHandleOption)option,
781                                    NULL);
782             if (!res)
783                 RETURN_ERROR(&tmp_err, -1, NULL);
784         } else {
785             // New callback object
786             Py_XINCREF(obj);
787             self->hmf_cb = obj;
788             res = lr_handle_setopt(self->handle,
789                                    &tmp_err,
790                                    (LrHandleOption)option,
791                                    hmf_callback);
792             if (!res)
793                 RETURN_ERROR(&tmp_err, -1, NULL);
794             res = lr_handle_setopt(self->handle,
795                                    &tmp_err,
796                                    LRO_PROGRESSDATA,
797                                    self);
798         }
799         break;
800     }
801 
802 
803     /*
804      * Options with callback data
805      */
806     case LRO_PROGRESSDATA: {
807         if (obj == Py_None) {
808             self->progress_cb_data = NULL;
809         } else {
810             Py_XINCREF(obj);
811             self->progress_cb_data = obj;
812         }
813         break;
814     }
815 
816     case LRO_FASTESTMIRRORDATA: {
817         if (obj == Py_None) {
818             self->fastestmirror_cb_data = NULL;
819         } else {
820             Py_XINCREF(obj);
821             self->fastestmirror_cb_data = obj;
822         }
823         break;
824     }
825 
826     /*
827      * Unknown options
828      */
829     default:
830         PyErr_SetString(PyExc_ValueError, "Unknown option");
831         return NULL;
832     }
833 
834     if (!res)
835         RETURN_ERROR(&tmp_err, -1, NULL);
836     Py_RETURN_NONE;
837 }
838 
839 static PyObject *
py_getinfo(_HandleObject * self,PyObject * args)840 py_getinfo(_HandleObject *self, PyObject *args)
841 {
842     int option;
843     gboolean res = TRUE;
844     char *str;
845     long lval;
846     double dval;
847     GError *tmp_err = NULL;
848 
849     if (!PyArg_ParseTuple(args, "i:py_getinfo", &option))
850         return NULL;
851     if (check_HandleStatus(self))
852         return NULL;
853 
854     switch (option) {
855 
856     /* char** options */
857     case LRI_MIRRORLIST:
858     case LRI_MIRRORLISTURL:
859     case LRI_METALINKURL:
860     case LRI_DESTDIR:
861     case LRI_USERAGENT:
862     case LRI_FASTESTMIRRORCACHE:
863     case LRI_GNUPGHOMEDIR:
864     case LRI_SSLCLIENTCERT:
865     case LRI_SSLCLIENTKEY:
866     case LRI_SSLCACERT:
867     case LRI_PROXY_SSLCLIENTCERT:
868     case LRI_PROXY_SSLCLIENTKEY:
869     case LRI_PROXY_SSLCACERT:
870     case LRI_CACHEDIR:
871         res = lr_handle_getinfo(self->handle,
872                                 &tmp_err,
873                                 (LrHandleInfoOption)option,
874                                 &str);
875         if (!res)
876             RETURN_ERROR(&tmp_err, -1, NULL);
877         return PyStringOrNone_FromString(str);
878 
879     /* double* options */
880     case LRI_FASTESTMIRRORTIMEOUT:
881         res = lr_handle_getinfo(self->handle,
882                                 &tmp_err,
883                                 (LrHandleInfoOption)option,
884                                 &dval);
885         if (!res)
886             RETURN_ERROR(&tmp_err, -1, NULL);
887         return PyFloat_FromDouble(dval);
888 
889     /* long* options */
890     case LRI_UPDATE:
891     case LRI_LOCAL:
892     case LRI_REPOTYPE:
893     case LRI_FETCHMIRRORS:
894     case LRI_MAXMIRRORTRIES:
895     case LRI_FASTESTMIRROR:
896     case LRI_FASTESTMIRRORMAXAGE:
897     case LRI_SSLVERIFYPEER:
898     case LRI_SSLVERIFYSTATUS:
899     case LRI_SSLVERIFYHOST:
900     case LRI_PROXY_SSLVERIFYPEER:
901     case LRI_PROXY_SSLVERIFYHOST:
902     case LRI_ALLOWEDMIRRORFAILURES:
903     case LRI_ADAPTIVEMIRRORSORTING:
904     case LRI_OFFLINE:
905     case LRI_LOWSPEEDTIME:
906     case LRI_LOWSPEEDLIMIT:
907     case LRI_FTPUSEEPSV:
908         res = lr_handle_getinfo(self->handle,
909                                 &tmp_err,
910                                 (LrHandleInfoOption)option,
911                                 &lval);
912         if (!res)
913             RETURN_ERROR(&tmp_err, -1, NULL);
914         return PyLong_FromLong(lval);
915 
916     /* LrAuth* option */
917     case LRI_HTTPAUTHMETHODS:
918     case LRI_PROXYAUTHMETHODS: {
919         LrAuth auth = 0;
920         res = lr_handle_getinfo(self->handle,
921                                 &tmp_err,
922                                 (LrHandleInfoOption)option,
923                                 &auth);
924         if (!res)
925             RETURN_ERROR(&tmp_err, -1, NULL);
926         return PyLong_FromLong((long) auth);
927     }
928 
929     /* LrIpResolveType* option  */
930     case LRI_IPRESOLVE: {
931         LrIpResolveType type;
932         res = lr_handle_getinfo(self->handle,
933                                 &tmp_err,
934                                 (LrHandleInfoOption)option,
935                                 &type);
936         if (!res)
937             RETURN_ERROR(&tmp_err, -1, NULL);
938         return PyLong_FromLong((long) type);
939     }
940 
941     /* List option */
942     case LRI_YUMSLIST:
943     case LRI_VARSUB: {
944         LrUrlVars *vars;
945         PyObject *list;
946 
947         res = lr_handle_getinfo(self->handle,
948                                 &tmp_err,
949                                 (LrHandleInfoOption)option,
950                                 &vars);
951         if (!res)
952             RETURN_ERROR(&tmp_err, -1, NULL);
953 
954         if (vars == NULL)
955             Py_RETURN_NONE;
956 
957         list = PyList_New(0);
958         for (LrUrlVars *elem = vars; elem; elem = g_slist_next(elem)) {
959             PyObject *tuple, *obj;
960             LrVar *var = elem->data;
961 
962             tuple = PyTuple_New(2);
963             obj = PyStringOrNone_FromString(var->var);
964             PyTuple_SetItem(tuple, 0, obj);
965 
966             if (var->val != NULL) {
967                 obj = PyStringOrNone_FromString(var->val);
968             } else {
969                 Py_INCREF(Py_None);
970                 obj = Py_None;
971             }
972 
973             PyTuple_SetItem(tuple, 1, obj);
974 
975             PyList_Append(list, tuple);
976         }
977         return list;
978     }
979 
980     /* char*** options */
981     case LRI_URLS:
982     case LRI_YUMDLIST:
983     case LRI_YUMBLIST:
984     case LRI_MIRRORS:
985     case LRI_HTTPHEADER:
986     {
987         PyObject *list;
988         char **strlist;
989         res = lr_handle_getinfo(self->handle,
990                                 &tmp_err,
991                                 (LrHandleInfoOption)option,
992                                 &strlist);
993         if (!res)
994             RETURN_ERROR(&tmp_err, -1, NULL);
995         if (strlist == NULL) {
996             if (option == LRI_MIRRORS || option == LRI_URLS) {
997                 return PyList_New(0);
998             } else {
999                 Py_RETURN_NONE;
1000             }
1001         }
1002         list = PyList_New(0);
1003         for (int x=0; strlist[x] != NULL; x++) {
1004             PyList_Append(list, PyStringOrNone_FromString(strlist[x]));
1005         }
1006 
1007         g_strfreev(strlist);
1008 
1009         return list;
1010     }
1011 
1012     /* callback option */
1013     case LRI_PROGRESSCB:
1014         if (self->progress_cb == NULL)
1015             Py_RETURN_NONE;
1016         Py_INCREF(self->progress_cb);
1017         return self->progress_cb;
1018 
1019     /* callback data options */
1020     case LRI_PROGRESSDATA:
1021         if (self->progress_cb_data == NULL)
1022             Py_RETURN_NONE;
1023         Py_INCREF(self->progress_cb_data);
1024         return self->progress_cb_data;
1025 
1026     case LRI_HMFCB:
1027         if (self->hmf_cb == NULL)
1028             Py_RETURN_NONE;
1029         Py_INCREF(self->hmf_cb);
1030         return self->hmf_cb;
1031 
1032     /* metalink */
1033     case LRI_METALINK: {
1034         PyObject *py_metalink;
1035         LrMetalink *metalink;
1036         res = lr_handle_getinfo(self->handle,
1037                                 &tmp_err,
1038                                 (LrHandleInfoOption)option,
1039                                 &metalink);
1040         if (!res)
1041             RETURN_ERROR(&tmp_err, -1, NULL);
1042         if (metalink == NULL)
1043             Py_RETURN_NONE;
1044         py_metalink = PyObject_FromMetalink(metalink);
1045         return py_metalink;
1046     }
1047 
1048     default:
1049         PyErr_SetString(PyExc_ValueError, "Unknown option");
1050         return NULL;
1051     }
1052 
1053     assert(res);
1054     Py_RETURN_NONE;
1055 }
1056 
1057 static PyObject *
py_perform(_HandleObject * self,PyObject * args)1058 py_perform(_HandleObject *self, PyObject *args)
1059 {
1060     PyObject *result_obj;
1061     LrResult *result;
1062     gboolean ret;
1063     GError *tmp_err = NULL;
1064     PyThreadState *state = NULL;
1065 
1066     if (!PyArg_ParseTuple(args, "O:py_perform", &result_obj))
1067         return NULL;
1068     if (check_HandleStatus(self))
1069         return NULL;
1070 
1071     result = Result_FromPyObject(result_obj);
1072 
1073     Handle_SetThreadState((PyObject *) self, &state);
1074 
1075     // XXX: GIL Hack
1076     int hack_rc = gil_logger_hack_begin(&state);
1077     if (hack_rc == GIL_HACK_ERROR)
1078         return NULL;
1079 
1080     BeginAllowThreads(&state);
1081     ret = lr_handle_perform(self->handle, result, &tmp_err);
1082     EndAllowThreads(&state);
1083 
1084     // XXX: GIL Hack
1085     if (!gil_logger_hack_end(hack_rc))
1086         return NULL;
1087 
1088     assert((ret && !tmp_err) || (!ret && tmp_err));
1089 
1090     if (ret)
1091         Py_RETURN_NONE; // All fine - Return None
1092 
1093     // Error occurred
1094     if (PyErr_Occurred()) {
1095         // Python exception occurred (in a python callback probably)
1096         return NULL;
1097     } else if(tmp_err->code == LRE_INTERRUPTED) {
1098         // Interrupted by Ctr+C
1099         g_error_free(tmp_err);
1100         PyErr_SetInterrupt();
1101         PyErr_CheckSignals();
1102         return NULL;
1103     } else {
1104         // Return exception created from GError
1105         RETURN_ERROR(&tmp_err, -1, NULL);
1106     }
1107 }
1108 
1109 static PyObject *
py_download_package(_HandleObject * self,PyObject * args)1110 py_download_package(_HandleObject *self, PyObject *args)
1111 {
1112     gboolean ret;
1113     char *relative_url, *checksum, *dest, *base_url;
1114     int resume, checksum_type;
1115     PY_LONG_LONG expectedsize;
1116     GError *tmp_err = NULL;
1117     PyThreadState *state = NULL;
1118 
1119     if (!PyArg_ParseTuple(args, "szizLzi:py_download_package", &relative_url,
1120                                                                &dest,
1121                                                                &checksum_type,
1122                                                                &checksum,
1123                                                                &expectedsize,
1124                                                                &base_url,
1125                                                                &resume))
1126         return NULL;
1127     if (check_HandleStatus(self))
1128         return NULL;
1129 
1130     Handle_SetThreadState((PyObject *) self, &state);
1131 
1132     // XXX: GIL Hack
1133     int hack_rc = gil_logger_hack_begin(&state);
1134     if (hack_rc == GIL_HACK_ERROR)
1135         return NULL;
1136 
1137     BeginAllowThreads(&state);
1138     ret = lr_download_package(self->handle, relative_url, dest, checksum_type,
1139                               checksum, (gint64) expectedsize, base_url,
1140                               resume, &tmp_err);
1141     EndAllowThreads(&state);
1142 
1143     // XXX: GIL Hack
1144     if (!gil_logger_hack_end(hack_rc))
1145         return NULL;
1146 
1147     assert((ret && !tmp_err) || (!ret && tmp_err));
1148 
1149     if (!ret && tmp_err->code == LRE_INTERRUPTED) {
1150         g_error_free(tmp_err);
1151         PyErr_SetInterrupt();
1152         PyErr_CheckSignals();
1153         return NULL;
1154     }
1155 
1156     if (ret)
1157         Py_RETURN_NONE; // All fine - Return None
1158 
1159     // Error occurred
1160     if (PyErr_Occurred()) {
1161         // Python exception occurred (in a python callback probably)
1162         return NULL;
1163     } else if(tmp_err->code == LRE_INTERRUPTED) {
1164         // Interrupted by Ctr+C
1165         g_error_free(tmp_err);
1166         PyErr_SetInterrupt();
1167         PyErr_CheckSignals();
1168         return NULL;
1169     } else {
1170         // Return exception created from GError
1171         RETURN_ERROR(&tmp_err, -1, NULL);
1172     }
1173 }
1174 
1175 static struct
1176 PyMethodDef handle_methods[] = {
1177     { "setopt", (PyCFunction)py_setopt, METH_VARARGS, NULL },
1178     { "getinfo", (PyCFunction)py_getinfo, METH_VARARGS, NULL },
1179     { "perform", (PyCFunction)py_perform, METH_VARARGS, NULL },
1180     { "download_package", (PyCFunction)py_download_package, METH_VARARGS, NULL },
1181     { NULL }
1182 };
1183 
1184 PyTypeObject Handle_Type = {
1185     PyVarObject_HEAD_INIT(NULL, 0)
1186     "_librepo.Handle",              /* tp_name */
1187     sizeof(_HandleObject),          /* tp_basicsize */
1188     0,                              /* tp_itemsize */
1189     (destructor) handle_dealloc,    /* tp_dealloc */
1190     0,                              /* tp_print */
1191     0,                              /* tp_getattr */
1192     0,                              /* tp_setattr */
1193     0,                              /* tp_compare */
1194     0,                              /* tp_repr */
1195     0,                              /* tp_as_number */
1196     0,                              /* tp_as_sequence */
1197     0,                              /* tp_as_mapping */
1198     0,                              /* tp_hash */
1199     0,                              /* tp_call */
1200     0,                              /* tp_str */
1201     0,                              /* tp_getattro */
1202     0,                              /* tp_setattro */
1203     0,                              /* tp_as_buffer */
1204     Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /* tp_flags */
1205     "Handle object",                /* tp_doc */
1206     0,                              /* tp_traverse */
1207     0,                              /* tp_clear */
1208     0,                              /* tp_richcompare */
1209     0,                              /* tp_weaklistoffset */
1210     PyObject_SelfIter,              /* tp_iter */
1211     0,                              /* tp_iternext */
1212     handle_methods,                 /* tp_methods */
1213     0,                              /* tp_members */
1214     0,                              /* tp_getset */
1215     0,                              /* tp_base */
1216     0,                              /* tp_dict */
1217     0,                              /* tp_descr_get */
1218     0,                              /* tp_descr_set */
1219     0,                              /* tp_dictoffset */
1220     (initproc) handle_init,         /* tp_init */
1221     0,                              /* tp_alloc */
1222     handle_new,                     /* tp_new */
1223     0,                              /* tp_free */
1224     0,                              /* tp_is_gc */
1225 };
1226