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