noggin 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/ext/noggin/noggin.c CHANGED
@@ -1,9 +1,14 @@
1
1
  #pragma GCC diagnostic ignored "-Wunused-but-set-variable"
2
- /* Includes */
3
- #include <ruby.h>
4
- #include <stdlib.h>
5
- #include <stdio.h>
6
- #include <string.h>
2
+ /* Includes */
3
+ #include <ruby.h>
4
+ #include <stdlib.h>
5
+ #include <stdio.h>
6
+ #include <string.h>
7
+ #if defined GCC
8
+ #define OPTIONAL_ATTR __attribute__((unused))
9
+ #else
10
+ #define OPTIONAL_ATTR
11
+ #endif
7
12
  #include "cups/cups.h"
8
13
  #include "cups/ipp.h"
9
14
  #include "ruby.h"
@@ -17,18 +22,24 @@ typedef int rubber_bool;
17
22
  /* Prototypes */
18
23
  static VALUE mNoggin;
19
24
  static VALUE
20
- Noggin_CLASS_destinations(VALUE self);
25
+ Noggin_CLASS_destinations(VALUE self OPTIONAL_ATTR );
21
26
  static VALUE
22
- Noggin_CLASS_jobs(VALUE self, VALUE printer, VALUE __v_mine, VALUE __v_whichjobs);
23
- static VALUE
24
- Noggin_CLASS_ippRequest(VALUE self, VALUE __v_operation, VALUE request_attributes);
27
+ Noggin_CLASS_jobs(VALUE self OPTIONAL_ATTR , VALUE printer OPTIONAL_ATTR, VALUE __v_mine OPTIONAL_ATTR, VALUE __v_whichjobs OPTIONAL_ATTR);
25
28
  static VALUE
26
29
  Noggin_CLASS_printFile(int __p_argc, VALUE *__p_argv, VALUE self);
27
30
  static VALUE mJob;
28
31
  static VALUE
29
- Job_CLASS_cancel(VALUE self, VALUE __v_printer, VALUE __v_job_id);
32
+ Job_CLASS_cancel(VALUE self OPTIONAL_ATTR , VALUE __v_printer OPTIONAL_ATTR, VALUE __v_job_id OPTIONAL_ATTR);
30
33
  static VALUE mIPP;
31
- static VALUE cIppRequest;
34
+ static VALUE mSubscription;
35
+ static VALUE
36
+ Subscription_CLASS_create(int __p_argc, VALUE *__p_argv, VALUE self);
37
+ static VALUE
38
+ Subscription_CLASS_renew(int __p_argc, VALUE *__p_argv, VALUE self);
39
+ static VALUE
40
+ Subscription_CLASS_cancel(VALUE self OPTIONAL_ATTR , VALUE __v_id OPTIONAL_ATTR);
41
+ static VALUE
42
+ Subscription_CLASS_list(int __p_argc, VALUE *__p_argv, VALUE self);
32
43
  static VALUE _gcpool_Keep = Qnil;
33
44
  static void __gcpool_Keep_add(VALUE val);
34
45
  static void __gcpool_Keep_del(VALUE val);
@@ -38,7 +49,7 @@ static void __gcpool_Keep_del(VALUE val);
38
49
  /* Inline C code */
39
50
 
40
51
 
41
- VALUE inline as_string(const char *string) { return string ? rb_str_new2(string) : Qnil; }
52
+ inline VALUE as_string(const char *string) { return string ? rb_str_new2(string) : Qnil; }
42
53
 
43
54
  #define TO_STRING(v) ((v) ? rb_str_new2((v)) : Qnil)
44
55
 
@@ -50,6 +61,7 @@ static VALUE sym_pending = Qnil;
50
61
  static VALUE sym_processing = Qnil;
51
62
  static VALUE sym_stopped = Qnil;
52
63
 
64
+ #define SUBSCRIPTION_DURATION 3600
53
65
  #define STATIC_STR(name) static VALUE str_##name = Qnil;
54
66
  #define STATIC_STR_INIT(name) KEEP_ADD(str_##name = rb_str_new2(#name)); rb_obj_freeze(str_##name);
55
67
  STATIC_STR(completed_time);
@@ -69,6 +81,72 @@ STATIC_STR(instance);
69
81
  STATIC_STR(is_default);
70
82
  STATIC_STR(options);
71
83
 
84
+ struct svp_it {
85
+ int num_options;
86
+ cups_option_t *options;
87
+ };
88
+
89
+ int hash_to_cups_options_it(VALUE key, VALUE val, VALUE data) {
90
+ struct svp_it *svp = (struct svp_it *)data;
91
+ svp->num_options = cupsAddOption(StringValuePtr(key), StringValuePtr(val), svp->num_options, &(svp->options));
92
+ return ST_CONTINUE;
93
+ }
94
+
95
+ static VALUE rb_ipp_value_entry(ipp_attribute_t* attr, int count) {
96
+ char *lang = NULL;
97
+ char block[4096] = "";
98
+
99
+ /*char block[4096] = "";
100
+ ippAttributeString(attr, block, 4096);
101
+ printf("%s: (0x%x) %s\n", ippGetName(attr), ippGetValueTag(attr), block);*/
102
+
103
+ switch (ippGetValueTag(attr)) {
104
+ case IPP_TAG_INTEGER:
105
+ return INT2NUM(ippGetInteger(attr, count));
106
+ case IPP_TAG_RESERVED_STRING:
107
+ case IPP_TAG_STRING:
108
+ case IPP_TAG_SUBSCRIPTION:
109
+ case IPP_TAG_TEXT:
110
+ case IPP_TAG_NAME:
111
+ case IPP_TAG_KEYWORD:
112
+ case IPP_TAG_URI:
113
+ case IPP_TAG_TEXTLANG:
114
+ case IPP_TAG_NAMELANG:
115
+ case IPP_TAG_LANGUAGE:
116
+ case IPP_TAG_CHARSET:
117
+ case IPP_TAG_MIMETYPE:
118
+ case IPP_TAG_MEMBERNAME:
119
+ case IPP_TAG_URISCHEME:
120
+ return as_string(ippGetString(attr, count, &lang));
121
+ case IPP_TAG_BOOLEAN:
122
+ return ippGetBoolean(attr, count) ? Qtrue : Qfalse;
123
+ default:
124
+ ippAttributeString(attr, block, 4096);
125
+ printf("[UNSUPPORTED] %s: (%i) %s\n", ippGetName(attr), ippGetValueTag(attr), block);
126
+
127
+ return Qnil;
128
+ }
129
+ }
130
+
131
+ static VALUE rb_ipp_value(ipp_attribute_t* attr) {
132
+ int num = ippGetCount(attr), i = 0;
133
+ VALUE val = Qnil;
134
+
135
+ switch (num) {
136
+ case 0:
137
+ return Qnil;
138
+ case 1:
139
+ return rb_ipp_value_entry(attr, 0);
140
+ default:
141
+ val = rb_ary_new();
142
+ for(i = 0; i < num; i++) {
143
+ rb_ary_push(val, rb_ipp_value_entry(attr, i));
144
+ }
145
+ return val;
146
+ }
147
+ }
148
+
149
+
72
150
  VALUE job_state(ipp_jstate_t state) {
73
151
  switch(state) {
74
152
  case IPP_JOB_ABORTED:
@@ -89,62 +167,190 @@ VALUE job_state(ipp_jstate_t state) {
89
167
  return INT2FIX(state);
90
168
  }
91
169
 
92
- VALUE rb_ipp_value(ipp_attribute_t* attr) {
93
- char *lang = NULL;
94
- switch (ippGetValueTag(attr)) {
95
- case IPP_TAG_INTEGER:
96
- return INT2NUM(ippGetInteger(attr, 0));
97
- case IPP_TAG_TEXT:
98
- case IPP_TAG_NAME:
99
- case IPP_TAG_URI:
100
- case IPP_TAG_URISCHEME:
101
- return as_string(ippGetString(attr, 0, &lang));
102
- case IPP_TAG_BOOLEAN:
103
- return ippGetBoolean(attr, 0) ? Qtrue : Qfalse;
170
+ static VALUE renew_subscription(int subscription_id, int duration) {
171
+ ipp_attribute_t *attr = NULL;
172
+ http_t *http;
173
+ ipp_t *request;
174
+ ipp_t *response;
175
+
176
+ if ((http = httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption ())) == NULL) {
177
+ return Qnil;
104
178
  }
105
- return Qnil;
106
179
 
180
+ request = ippNewRequest (IPP_RENEW_SUBSCRIPTION);
181
+ ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
182
+ "printer-uri", NULL, "/");
183
+ ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
184
+ "requesting-user-name", NULL, cupsUser());
185
+ ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
186
+ "notify-subscription-id", subscription_id);
187
+ ippAddInteger (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
188
+ "notify-lease-duration", duration);
189
+ ippDelete (cupsDoRequest (http, request, "/"));
190
+
191
+ httpClose(http);
192
+
193
+ return INT2NUM(subscription_id);
107
194
  }
108
195
 
109
- struct svp_it {
110
- int num_options;
111
- cups_option_t *options;
112
- };
196
+ static void debug_ipp(ipp_t *ipp) {
197
+ char block[4096] = "";
198
+ ipp_attribute_t *attr = NULL;
199
+
200
+ for (attr = ippFirstAttribute(ipp); attr; attr = ippNextAttribute(ipp)) {
201
+ ippAttributeString(attr, block, 4096);
202
+ printf("[DEBUG] %s: (%i) %s\n", ippGetName(attr), ippGetValueTag(attr), block);
203
+ }
204
+
205
+ }
113
206
 
114
- int hash_to_cups_options_it(VALUE key, VALUE val, VALUE data) {
115
- struct svp_it *svp = (struct svp_it *)data;
207
+ static VALUE create_subscription(int duration, char *uri, char *printer) {
208
+ ipp_attribute_t *attr = NULL;
209
+ http_t *http;
210
+ ipp_t *request;
211
+ ipp_t *response;
212
+ VALUE subscription_id = 0;
213
+ int num_events = 7;
214
+ static const char * const events[] = {
215
+ "job-created",
216
+ "job-completed",
217
+ "job-state-changed",
218
+ "job-state",
219
+ "printer-added",
220
+ "printer-deleted",
221
+ "printer-state-changed"
222
+ };
223
+
224
+ if ((http = httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption())) == NULL) {
225
+ return Qnil;
226
+ }
116
227
 
117
- svp->num_options = cupsAddOption(StringValuePtr(key), StringValuePtr(val), svp->num_options, &(svp->options));
228
+ request = ippNewRequest (IPP_CREATE_PRINTER_SUBSCRIPTION);
229
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
230
+ "printer-uri", NULL, printer);
231
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
232
+ "requesting-user-name", NULL, cupsUser ());
233
+ ippAddStrings(request, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
234
+ "notify-events", num_events, NULL, events);
235
+ ippAddString(request, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
236
+ "notify-pull-method", NULL, "ippget");
237
+ ippAddString(request, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
238
+ "notify-recipient-uri", NULL, uri);
239
+ ippAddInteger(request, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
240
+ "notify-lease-duration", duration);
241
+ response = cupsDoRequest (http, request, "/");
242
+
243
+ if (response != NULL && ippGetStatusCode(response) <= IPP_OK_CONFLICT) {
244
+ /*debug_ipp(response);*/
245
+ ippFirstAttribute(response);
246
+
247
+ if ((attr = ippFindAttribute(response, "notify-subscription-id", IPP_TAG_INTEGER)) != NULL) {
248
+ subscription_id = INT2NUM(ippGetInteger(attr, 0));
249
+ } else {
250
+ subscription_id = Qnil;
251
+ }
252
+ }
118
253
 
119
- return ST_CONTINUE;
254
+ if (response) {
255
+ ippDelete(response);
256
+ }
257
+
258
+ httpClose (http);
259
+
260
+ return subscription_id;
120
261
  }
121
- int add_to_request_iterator(VALUE key, VALUE val, VALUE data) {
122
- ipp_t *request = (ipp_t*) data;
123
- char *name = RSTRING_PTR(key);
124
-
125
- switch(TYPE(val)) {
126
- case T_FIXNUM:
127
- ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, name, NUM2INT(val));
128
- break;
129
- case T_STRING:
130
- ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, name, NULL, RSTRING_PTR(val));
131
- break;
132
- case T_TRUE:
133
- case T_FALSE:
134
- ippAddBoolean(request, IPP_TAG_OPERATION, name, (val) ? 1 : 0);
135
- break;
262
+
263
+ static void
264
+ cancel_subscription(int subscription_id)
265
+ {
266
+ http_t *http;
267
+ ipp_t *request;
268
+
269
+ if (subscription_id >= 0 &&
270
+ ((http = httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption())) != NULL)) {
271
+
272
+ request = ippNewRequest(IPP_CANCEL_SUBSCRIPTION);
273
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
274
+ "printer-uri", NULL, "/");
275
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
276
+ "requesting-user-name", NULL, cupsUser());
277
+ ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
278
+ "notify-subscription-id", subscription_id);
279
+ ippDelete(cupsDoRequest(http, request, "/"));
280
+
281
+ httpClose(http);
136
282
  }
137
- return ST_CONTINUE;
283
+ }
284
+
285
+ static VALUE list_subscriptions(bool my_subscriptions) {
286
+ ipp_attribute_t *attr = NULL;
287
+ http_t *http;
288
+ ipp_t *request;
289
+ ipp_t *response;
290
+ VALUE subscription_id = 0;
291
+ VALUE ary = rb_ary_new();
292
+ static const char * const req_attr[] = {
293
+ "all"
294
+ };
295
+
296
+ if ((http = httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption ())) == NULL) {
297
+ return Qnil;
298
+ }
299
+
300
+ request = ippNewRequest (IPP_GET_SUBSCRIPTIONS);
301
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
302
+ "printer-uri", NULL, "/");
303
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
304
+ "requesting-user-name", NULL, cupsUser ());
305
+ ippAddStrings(request, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
306
+ "requested-attributes", 1, NULL, req_attr);
307
+ ippAddBoolean(request, IPP_TAG_SUBSCRIPTION, "my-subscriptions",
308
+ my_subscriptions);
309
+ response = cupsDoRequest (http, request, "/");
310
+
311
+ if (response != NULL && ippGetStatusCode(response) <= IPP_OK_CONFLICT) {
312
+ char block[4096] = "", *name;
313
+ VALUE hash = rb_hash_new();
314
+
315
+ attr = ippFirstAttribute(response);
316
+ ippNextAttribute(response);
317
+ ippNextAttribute(response);
318
+
319
+ rb_ary_push(ary, hash);
320
+
321
+ for (; attr != NULL; attr = ippNextAttribute(response)) {
322
+ name = ippGetName(attr);
323
+
324
+ if (name == NULL) {
325
+ hash = rb_hash_new();
326
+ rb_ary_push(ary, hash);
327
+ } else {
328
+ rb_hash_aset(hash, as_string(name), rb_ipp_value(attr));
329
+ /*puts(ippGetName(attr));
330
+ if (ippAttributeString(attr, block, 4096) > 0) {
331
+ puts(block);
332
+ }*/
333
+ }
334
+ }
335
+ }
336
+
337
+ if (response) {
338
+ ippDelete(response);
339
+ }
340
+
341
+ httpClose (http);
342
+
343
+ return ary;
138
344
  }
139
345
 
140
346
 
141
347
  /* Code */
142
348
  static VALUE
143
- Noggin_CLASS_destinations(VALUE self)
349
+ Noggin_CLASS_destinations(VALUE self OPTIONAL_ATTR )
144
350
  {
145
- VALUE __p_retval = Qnil;
351
+ VALUE __p_retval OPTIONAL_ATTR = Qnil;
146
352
 
147
- #line 161 "/home/geoff/Projects/noggin/ext/noggin/noggin.cr"
353
+ #line 356 "/home/geoff/Projects/noggin/ext/noggin/noggin.cr"
148
354
 
149
355
  do {
150
356
  VALUE list =
@@ -178,9 +384,9 @@ out:
178
384
  }
179
385
 
180
386
  static VALUE
181
- Noggin_CLASS_jobs(VALUE self, VALUE printer, VALUE __v_mine, VALUE __v_whichjobs)
387
+ Noggin_CLASS_jobs(VALUE self OPTIONAL_ATTR , VALUE printer OPTIONAL_ATTR, VALUE __v_mine OPTIONAL_ATTR, VALUE __v_whichjobs OPTIONAL_ATTR)
182
388
  {
183
- VALUE __p_retval = Qnil;
389
+ VALUE __p_retval OPTIONAL_ATTR = Qnil;
184
390
  bool mine; bool __orig_mine;
185
391
  int whichjobs; int __orig_whichjobs;
186
392
  if (! ((TYPE(printer) == T_NIL) || (TYPE(printer) == T_STRING)) )
@@ -188,7 +394,7 @@ Noggin_CLASS_jobs(VALUE self, VALUE printer, VALUE __v_mine, VALUE __v_whichjobs
188
394
  __orig_mine = mine = RTEST(__v_mine);
189
395
  __orig_whichjobs = whichjobs = NUM2INT(__v_whichjobs);
190
396
 
191
- #line 192 "/home/geoff/Projects/noggin/ext/noggin/noggin.cr"
397
+ #line 386 "/home/geoff/Projects/noggin/ext/noggin/noggin.cr"
192
398
 
193
399
  do {
194
400
  VALUE list =
@@ -222,41 +428,10 @@ out:
222
428
  return __p_retval;
223
429
  }
224
430
 
225
- static VALUE
226
- Noggin_CLASS_ippRequest(VALUE self, VALUE __v_operation, VALUE request_attributes)
227
- {
228
- VALUE __p_retval = Qnil;
229
- int operation; int __orig_operation;
230
- __orig_operation = operation = NUM2INT(__v_operation);
231
-
232
- #line 227 "/home/geoff/Projects/noggin/ext/noggin/noggin.cr"
233
-
234
- do {
235
- VALUE resp =
236
- Qnil;
237
- ipp_t * request =
238
- NULL , *response = NULL;
239
- ipp_attribute_t * attr =
240
- NULL;
241
- request = ippNewRequest(operation);
242
- rb_hash_foreach(request_attributes, add_to_request_iterator, (VALUE)request);
243
- response = cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/");
244
- resp = rb_hash_new();
245
- for (attr = ippFirstAttribute(response);
246
- attr != NULL;
247
- attr = ippNextAttribute(response)) { rb_hash_aset(resp, as_string(ippGetName(attr)), rb_ipp_value(attr));
248
- } do { __p_retval = resp; goto out; } while(0);
249
-
250
- } while(0);
251
-
252
- out:
253
- return __p_retval;
254
- }
255
-
256
431
  static VALUE
257
432
  Noggin_CLASS_printFile(int __p_argc, VALUE *__p_argv, VALUE self)
258
433
  {
259
- VALUE __p_retval = Qnil;
434
+ VALUE __p_retval OPTIONAL_ATTR = Qnil;
260
435
  VALUE __v_destinationName = Qnil;
261
436
  char * destinationName; char * __orig_destinationName;
262
437
  VALUE __v_fileName = Qnil;
@@ -282,7 +457,7 @@ Noggin_CLASS_printFile(int __p_argc, VALUE *__p_argv, VALUE self)
282
457
  rb_raise(rb_eArgError, "options argument must be one of Hash, Nil");
283
458
 
284
459
 
285
- #line 246 "/home/geoff/Projects/noggin/ext/noggin/noggin.cr"
460
+ #line 417 "/home/geoff/Projects/noggin/ext/noggin/noggin.cr"
286
461
 
287
462
  do {
288
463
  struct svp_it info =
@@ -300,19 +475,118 @@ out:
300
475
  }
301
476
 
302
477
  static VALUE
303
- Job_CLASS_cancel(VALUE self, VALUE __v_printer, VALUE __v_job_id)
478
+ Job_CLASS_cancel(VALUE self OPTIONAL_ATTR , VALUE __v_printer OPTIONAL_ATTR, VALUE __v_job_id OPTIONAL_ATTR)
304
479
  {
305
480
  char * printer; char * __orig_printer;
306
481
  int job_id; int __orig_job_id;
307
482
  __orig_printer = printer = ( NIL_P(__v_printer) ? NULL : StringValuePtr(__v_printer) );
308
483
  __orig_job_id = job_id = NUM2INT(__v_job_id);
309
484
 
310
- #line 152 "/home/geoff/Projects/noggin/ext/noggin/noggin.cr"
485
+ #line 347 "/home/geoff/Projects/noggin/ext/noggin/noggin.cr"
311
486
  if (cupsCancelJob(printer, job_id) == 0) { rb_raise(rb_eRuntimeError, "CUPS Error: %d - %s", cupsLastError(), cupsLastErrorString());
312
487
  }
313
488
  return Qnil;
314
489
  }
315
490
 
491
+ static VALUE
492
+ Subscription_CLASS_create(int __p_argc, VALUE *__p_argv, VALUE self)
493
+ {
494
+ VALUE __p_retval OPTIONAL_ATTR = Qnil;
495
+ VALUE __v_duration = Qnil;
496
+ int duration; int __orig_duration;
497
+ VALUE __v_notify_uri = Qnil;
498
+ char * notify_uri; char * __orig_notify_uri;
499
+ VALUE __v_printer = Qnil;
500
+ char * printer; char * __orig_printer;
501
+
502
+ /* Scan arguments */
503
+ rb_scan_args(__p_argc, __p_argv, "03",&__v_duration, &__v_notify_uri, &__v_printer);
504
+
505
+ /* Set defaults */
506
+ if (__p_argc > 0)
507
+ __orig_duration = duration = NUM2INT(__v_duration);
508
+ else
509
+ duration = 0;
510
+
511
+ if (__p_argc > 1)
512
+ __orig_notify_uri = notify_uri = ( NIL_P(__v_notify_uri) ? NULL : StringValuePtr(__v_notify_uri) );
513
+ else
514
+ notify_uri = "dbus://";
515
+
516
+ if (__p_argc > 2)
517
+ __orig_printer = printer = ( NIL_P(__v_printer) ? NULL : StringValuePtr(__v_printer) );
518
+ else
519
+ printer = "/";
520
+
521
+
522
+ #line 435 "/home/geoff/Projects/noggin/ext/noggin/noggin.cr"
523
+ do { __p_retval = create_subscription(duration, notify_uri, printer); goto out; } while(0);
524
+ out:
525
+ return __p_retval;
526
+ }
527
+
528
+ static VALUE
529
+ Subscription_CLASS_renew(int __p_argc, VALUE *__p_argv, VALUE self)
530
+ {
531
+ VALUE __p_retval OPTIONAL_ATTR = Qnil;
532
+ VALUE __v_id = Qnil;
533
+ int id; int __orig_id;
534
+ VALUE __v_duration = Qnil;
535
+ int duration; int __orig_duration;
536
+
537
+ /* Scan arguments */
538
+ rb_scan_args(__p_argc, __p_argv, "11",&__v_id, &__v_duration);
539
+
540
+ /* Set defaults */
541
+ __orig_id = id = NUM2INT(__v_id);
542
+
543
+ if (__p_argc > 1)
544
+ __orig_duration = duration = NUM2INT(__v_duration);
545
+ else
546
+ duration = 0;
547
+
548
+
549
+ #line 438 "/home/geoff/Projects/noggin/ext/noggin/noggin.cr"
550
+ do { __p_retval = renew_subscription(id, duration); goto out; } while(0);
551
+ out:
552
+ return __p_retval;
553
+ }
554
+
555
+ static VALUE
556
+ Subscription_CLASS_cancel(VALUE self OPTIONAL_ATTR , VALUE __v_id OPTIONAL_ATTR)
557
+ {
558
+ int id; int __orig_id;
559
+ __orig_id = id = NUM2INT(__v_id);
560
+
561
+ #line 441 "/home/geoff/Projects/noggin/ext/noggin/noggin.cr"
562
+ cancel_subscription(id);
563
+
564
+ return Qnil;
565
+ }
566
+
567
+ static VALUE
568
+ Subscription_CLASS_list(int __p_argc, VALUE *__p_argv, VALUE self)
569
+ {
570
+ VALUE __p_retval OPTIONAL_ATTR = Qnil;
571
+ VALUE __v_my_subscriptions = Qnil;
572
+ bool my_subscriptions; bool __orig_my_subscriptions;
573
+
574
+ /* Scan arguments */
575
+ rb_scan_args(__p_argc, __p_argv, "01",&__v_my_subscriptions);
576
+
577
+ /* Set defaults */
578
+ if (__p_argc > 0)
579
+ __orig_my_subscriptions = my_subscriptions = RTEST(__v_my_subscriptions);
580
+ else
581
+ my_subscriptions = 1;
582
+
583
+
584
+ #line 444 "/home/geoff/Projects/noggin/ext/noggin/noggin.cr"
585
+ do { __p_retval = list_subscriptions(my_subscriptions); goto out; } while(0);
586
+ out:
587
+ return __p_retval;
588
+ }
589
+
316
590
  static void __gcpool_Keep_add(VALUE val)
317
591
  {
318
592
  if (_gcpool_Keep == Qnil)
@@ -345,7 +619,6 @@ Init_noggin(void)
345
619
  mNoggin = rb_define_module("Noggin");
346
620
  rb_define_singleton_method(mNoggin, "destinations", Noggin_CLASS_destinations, 0);
347
621
  rb_define_singleton_method(mNoggin, "jobs", Noggin_CLASS_jobs, 3);
348
- rb_define_singleton_method(mNoggin, "ippRequest", Noggin_CLASS_ippRequest, 2);
349
622
  rb_define_singleton_method(mNoggin, "printFile", Noggin_CLASS_printFile, -1);
350
623
  rb_define_const(mNoggin, "WHICHJOBS_ALL", INT2NUM(CUPS_WHICHJOBS_ALL));
351
624
  rb_define_const(mNoggin, "WHICHJOBS_ACTIVE", INT2NUM(CUPS_WHICHJOBS_ACTIVE));
@@ -356,7 +629,11 @@ Init_noggin(void)
356
629
  rb_define_const(mJob, "ALL", INT2NUM(CUPS_JOBID_ALL));
357
630
  mIPP = rb_define_module_under(mNoggin, "IPP");
358
631
  rb_define_const(mIPP, "GET_JOBS", INT2NUM(IPP_GET_JOBS));
359
- cIppRequest = rb_define_class_under(mNoggin, "IppRequest", rb_cObject);
632
+ mSubscription = rb_define_module_under(mNoggin, "Subscription");
633
+ rb_define_singleton_method(mSubscription, "create", Subscription_CLASS_create, -1);
634
+ rb_define_singleton_method(mSubscription, "renew", Subscription_CLASS_renew, -1);
635
+ rb_define_singleton_method(mSubscription, "cancel", Subscription_CLASS_cancel, 1);
636
+ rb_define_singleton_method(mSubscription, "list", Subscription_CLASS_list, -1);
360
637
  rb_gc_register_address(&_gcpool_Keep);
361
638
 
362
639
  KEEP_ADD(sym_cancelled = ID2SYM(rb_intern("cancelled")));
data/ext/noggin/noggin.cr CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  %{
10
10
 
11
- VALUE inline as_string(const char *string) { return string ? rb_str_new2(string) : Qnil; }
11
+ inline VALUE as_string(const char *string) { return string ? rb_str_new2(string) : Qnil; }
12
12
 
13
13
  #define TO_STRING(v) ((v) ? rb_str_new2((v)) : Qnil)
14
14
 
@@ -20,6 +20,7 @@ static VALUE sym_pending = Qnil;
20
20
  static VALUE sym_processing = Qnil;
21
21
  static VALUE sym_stopped = Qnil;
22
22
 
23
+ #define SUBSCRIPTION_DURATION 3600
23
24
  #define STATIC_STR(name) static VALUE str_##name = Qnil;
24
25
  #define STATIC_STR_INIT(name) KEEP_ADD(str_##name = rb_str_new2(#name)); rb_obj_freeze(str_##name);
25
26
  STATIC_STR(completed_time);
@@ -39,6 +40,72 @@ STATIC_STR(instance);
39
40
  STATIC_STR(is_default);
40
41
  STATIC_STR(options);
41
42
 
43
+ struct svp_it {
44
+ int num_options;
45
+ cups_option_t *options;
46
+ };
47
+
48
+ int hash_to_cups_options_it(VALUE key, VALUE val, VALUE data) {
49
+ struct svp_it *svp = (struct svp_it *)data;
50
+ svp->num_options = cupsAddOption(StringValuePtr(key), StringValuePtr(val), svp->num_options, &(svp->options));
51
+ return ST_CONTINUE;
52
+ }
53
+
54
+ static VALUE rb_ipp_value_entry(ipp_attribute_t* attr, int count) {
55
+ char *lang = NULL;
56
+ char block[4096] = "";
57
+
58
+ /*char block[4096] = "";
59
+ ippAttributeString(attr, block, 4096);
60
+ printf("%s: (0x%x) %s\n", ippGetName(attr), ippGetValueTag(attr), block);*/
61
+
62
+ switch (ippGetValueTag(attr)) {
63
+ case IPP_TAG_INTEGER:
64
+ return INT2NUM(ippGetInteger(attr, count));
65
+ case IPP_TAG_RESERVED_STRING:
66
+ case IPP_TAG_STRING:
67
+ case IPP_TAG_SUBSCRIPTION:
68
+ case IPP_TAG_TEXT:
69
+ case IPP_TAG_NAME:
70
+ case IPP_TAG_KEYWORD:
71
+ case IPP_TAG_URI:
72
+ case IPP_TAG_TEXTLANG:
73
+ case IPP_TAG_NAMELANG:
74
+ case IPP_TAG_LANGUAGE:
75
+ case IPP_TAG_CHARSET:
76
+ case IPP_TAG_MIMETYPE:
77
+ case IPP_TAG_MEMBERNAME:
78
+ case IPP_TAG_URISCHEME:
79
+ return as_string(ippGetString(attr, count, &lang));
80
+ case IPP_TAG_BOOLEAN:
81
+ return ippGetBoolean(attr, count) ? Qtrue : Qfalse;
82
+ default:
83
+ ippAttributeString(attr, block, 4096);
84
+ printf("[UNSUPPORTED] %s: (%i) %s\n", ippGetName(attr), ippGetValueTag(attr), block);
85
+
86
+ return Qnil;
87
+ }
88
+ }
89
+
90
+ static VALUE rb_ipp_value(ipp_attribute_t* attr) {
91
+ int num = ippGetCount(attr), i = 0;
92
+ VALUE val = Qnil;
93
+
94
+ switch (num) {
95
+ case 0:
96
+ return Qnil;
97
+ case 1:
98
+ return rb_ipp_value_entry(attr, 0);
99
+ default:
100
+ val = rb_ary_new();
101
+ for(i = 0; i < num; i++) {
102
+ rb_ary_push(val, rb_ipp_value_entry(attr, i));
103
+ }
104
+ return val;
105
+ }
106
+ }
107
+
108
+
42
109
  VALUE job_state(ipp_jstate_t state) {
43
110
  switch(state) {
44
111
  case IPP_JOB_ABORTED:
@@ -59,52 +126,180 @@ VALUE job_state(ipp_jstate_t state) {
59
126
  return INT2FIX(state);
60
127
  }
61
128
 
62
- VALUE rb_ipp_value(ipp_attribute_t* attr) {
63
- char *lang = NULL;
64
- switch (ippGetValueTag(attr)) {
65
- case IPP_TAG_INTEGER:
66
- return INT2NUM(ippGetInteger(attr, 0));
67
- case IPP_TAG_TEXT:
68
- case IPP_TAG_NAME:
69
- case IPP_TAG_URI:
70
- case IPP_TAG_URISCHEME:
71
- return as_string(ippGetString(attr, 0, &lang));
72
- case IPP_TAG_BOOLEAN:
73
- return ippGetBoolean(attr, 0) ? Qtrue : Qfalse;
129
+ static VALUE renew_subscription(int subscription_id, int duration) {
130
+ ipp_attribute_t *attr = NULL;
131
+ http_t *http;
132
+ ipp_t *request;
133
+ ipp_t *response;
134
+
135
+ if ((http = httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption ())) == NULL) {
136
+ return Qnil;
74
137
  }
75
- return Qnil;
76
138
 
139
+ request = ippNewRequest (IPP_RENEW_SUBSCRIPTION);
140
+ ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
141
+ "printer-uri", NULL, "/");
142
+ ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
143
+ "requesting-user-name", NULL, cupsUser());
144
+ ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
145
+ "notify-subscription-id", subscription_id);
146
+ ippAddInteger (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
147
+ "notify-lease-duration", duration);
148
+ ippDelete (cupsDoRequest (http, request, "/"));
149
+
150
+ httpClose(http);
151
+
152
+ return INT2NUM(subscription_id);
77
153
  }
78
154
 
79
- struct svp_it {
80
- int num_options;
81
- cups_option_t *options;
82
- };
155
+ static void debug_ipp(ipp_t *ipp) {
156
+ char block[4096] = "";
157
+ ipp_attribute_t *attr = NULL;
158
+
159
+ for (attr = ippFirstAttribute(ipp); attr; attr = ippNextAttribute(ipp)) {
160
+ ippAttributeString(attr, block, 4096);
161
+ printf("[DEBUG] %s: (%i) %s\n", ippGetName(attr), ippGetValueTag(attr), block);
162
+ }
163
+
164
+ }
83
165
 
84
- int hash_to_cups_options_it(VALUE key, VALUE val, VALUE data) {
85
- struct svp_it *svp = (struct svp_it *)data;
166
+ static VALUE create_subscription(int duration, char *uri, char *printer) {
167
+ ipp_attribute_t *attr = NULL;
168
+ http_t *http;
169
+ ipp_t *request;
170
+ ipp_t *response;
171
+ VALUE subscription_id = 0;
172
+ int num_events = 7;
173
+ static const char * const events[] = {
174
+ "job-created",
175
+ "job-completed",
176
+ "job-state-changed",
177
+ "job-state",
178
+ "printer-added",
179
+ "printer-deleted",
180
+ "printer-state-changed"
181
+ };
182
+
183
+ if ((http = httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption())) == NULL) {
184
+ return Qnil;
185
+ }
86
186
 
87
- svp->num_options = cupsAddOption(StringValuePtr(key), StringValuePtr(val), svp->num_options, &(svp->options));
187
+ request = ippNewRequest (IPP_CREATE_PRINTER_SUBSCRIPTION);
188
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
189
+ "printer-uri", NULL, printer);
190
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
191
+ "requesting-user-name", NULL, cupsUser ());
192
+ ippAddStrings(request, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
193
+ "notify-events", num_events, NULL, events);
194
+ ippAddString(request, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
195
+ "notify-pull-method", NULL, "ippget");
196
+ ippAddString(request, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
197
+ "notify-recipient-uri", NULL, uri);
198
+ ippAddInteger(request, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
199
+ "notify-lease-duration", duration);
200
+ response = cupsDoRequest (http, request, "/");
201
+
202
+ if (response != NULL && ippGetStatusCode(response) <= IPP_OK_CONFLICT) {
203
+ /*debug_ipp(response);*/
204
+ ippFirstAttribute(response);
205
+
206
+ if ((attr = ippFindAttribute(response, "notify-subscription-id", IPP_TAG_INTEGER)) != NULL) {
207
+ subscription_id = INT2NUM(ippGetInteger(attr, 0));
208
+ } else {
209
+ subscription_id = Qnil;
210
+ }
211
+ }
88
212
 
89
- return ST_CONTINUE;
213
+ if (response) {
214
+ ippDelete(response);
215
+ }
216
+
217
+ httpClose (http);
218
+
219
+ return subscription_id;
90
220
  }
91
- int add_to_request_iterator(VALUE key, VALUE val, VALUE data) {
92
- ipp_t *request = (ipp_t*) data;
93
- char *name = RSTRING_PTR(key);
94
-
95
- switch(TYPE(val)) {
96
- case T_FIXNUM:
97
- ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, name, NUM2INT(val));
98
- break;
99
- case T_STRING:
100
- ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, name, NULL, RSTRING_PTR(val));
101
- break;
102
- case T_TRUE:
103
- case T_FALSE:
104
- ippAddBoolean(request, IPP_TAG_OPERATION, name, (val) ? 1 : 0);
105
- break;
221
+
222
+ static void
223
+ cancel_subscription(int subscription_id)
224
+ {
225
+ http_t *http;
226
+ ipp_t *request;
227
+
228
+ if (subscription_id >= 0 &&
229
+ ((http = httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption())) != NULL)) {
230
+
231
+ request = ippNewRequest(IPP_CANCEL_SUBSCRIPTION);
232
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
233
+ "printer-uri", NULL, "/");
234
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
235
+ "requesting-user-name", NULL, cupsUser());
236
+ ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
237
+ "notify-subscription-id", subscription_id);
238
+ ippDelete(cupsDoRequest(http, request, "/"));
239
+
240
+ httpClose(http);
106
241
  }
107
- return ST_CONTINUE;
242
+ }
243
+
244
+ static VALUE list_subscriptions(bool my_subscriptions) {
245
+ ipp_attribute_t *attr = NULL;
246
+ http_t *http;
247
+ ipp_t *request;
248
+ ipp_t *response;
249
+ VALUE subscription_id = 0;
250
+ VALUE ary = rb_ary_new();
251
+ static const char * const req_attr[] = {
252
+ "all"
253
+ };
254
+
255
+ if ((http = httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption ())) == NULL) {
256
+ return Qnil;
257
+ }
258
+
259
+ request = ippNewRequest (IPP_GET_SUBSCRIPTIONS);
260
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
261
+ "printer-uri", NULL, "/");
262
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
263
+ "requesting-user-name", NULL, cupsUser ());
264
+ ippAddStrings(request, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
265
+ "requested-attributes", 1, NULL, req_attr);
266
+ ippAddBoolean(request, IPP_TAG_SUBSCRIPTION, "my-subscriptions",
267
+ my_subscriptions);
268
+ response = cupsDoRequest (http, request, "/");
269
+
270
+ if (response != NULL && ippGetStatusCode(response) <= IPP_OK_CONFLICT) {
271
+ char block[4096] = "", *name;
272
+ VALUE hash = rb_hash_new();
273
+
274
+ attr = ippFirstAttribute(response);
275
+ ippNextAttribute(response);
276
+ ippNextAttribute(response);
277
+
278
+ rb_ary_push(ary, hash);
279
+
280
+ for (; attr != NULL; attr = ippNextAttribute(response)) {
281
+ name = ippGetName(attr);
282
+
283
+ if (name == NULL) {
284
+ hash = rb_hash_new();
285
+ rb_ary_push(ary, hash);
286
+ } else {
287
+ rb_hash_aset(hash, as_string(name), rb_ipp_value(attr));
288
+ /*puts(ippGetName(attr));
289
+ if (ippAttributeString(attr, block, 4096) > 0) {
290
+ puts(block);
291
+ }*/
292
+ }
293
+ }
294
+ }
295
+
296
+ if (response) {
297
+ ippDelete(response);
298
+ }
299
+
300
+ httpClose (http);
301
+
302
+ return ary;
108
303
  }
109
304
 
110
305
  %}
@@ -180,7 +375,6 @@ module Noggin
180
375
  rb_hash_aset(options, as_string(dest->options[j].name), as_string(dest->options[j].value));
181
376
  }
182
377
 
183
-
184
378
  rb_ary_push(list, hash);
185
379
  }
186
380
 
@@ -220,29 +414,6 @@ module Noggin
220
414
  return list;
221
415
  end
222
416
 
223
- class IppRequest
224
-
225
- end
226
-
227
- def self.ippRequest(int operation, VALUE request_attributes)
228
- VALUE resp = Qnil;
229
- ipp_t *request = NULL , *response = NULL;
230
- ipp_attribute_t *attr = NULL;
231
-
232
- request = ippNewRequest(operation);
233
- rb_hash_foreach(request_attributes, add_to_request_iterator, (VALUE)request);
234
-
235
- response = cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/");
236
-
237
- resp = rb_hash_new();
238
-
239
- for (attr = ippFirstAttribute(response); attr != NULL; attr = ippNextAttribute(response)) {
240
- rb_hash_aset(resp, as_string(ippGetName(attr)), rb_ipp_value(attr));
241
- }
242
-
243
- return resp;
244
- end
245
-
246
417
  def int:self.printFile(char *destinationName, char *fileName, char *title, T_HASH|T_NIL options = Qnil)
247
418
  struct svp_it info = {0, NULL};
248
419
  int job_id = -1;
@@ -259,4 +430,19 @@ module Noggin
259
430
 
260
431
  return job_id;
261
432
  end
433
+
434
+ module Subscription
435
+ def self.create(int duration=0, char *notify_uri="dbus://", char *printer="/")
436
+ return create_subscription(duration, notify_uri, printer);
437
+ end
438
+ def self.renew(int id, int duration=0)
439
+ return renew_subscription(id, duration);
440
+ end
441
+ def self.cancel(int id)
442
+ cancel_subscription(id);
443
+ end
444
+ def self.list(bool my_subscriptions=1)
445
+ return list_subscriptions(my_subscriptions);
446
+ end
447
+ end
262
448
  end
data/ext/noggin/noggin.rd CHANGED
@@ -5,9 +5,6 @@
5
5
  --- Noggin.jobs(String printer, Boolean mine, Integer whichjobs)
6
6
 
7
7
 
8
- --- Noggin.ippRequest(Integer operation, request_attributes)
9
-
10
-
11
8
  --- Noggin.printFile(String destinationName, String fileName, String title, Hash options)
12
9
 
13
10
 
@@ -22,4 +19,16 @@
22
19
  === Noggin::Job::ALL
23
20
  == module Noggin::IPP
24
21
  === Noggin::IPP::GET_JOBS
25
- == class Noggin::IppRequest
22
+ == module Noggin::Subscription
23
+ --- Noggin::Subscription.create(Integer duration, String notify_uri, String printer)
24
+
25
+
26
+ --- Noggin::Subscription.renew(Integer id, Integer duration)
27
+
28
+
29
+ --- Noggin::Subscription.cancel(Integer id)
30
+
31
+
32
+ --- Noggin::Subscription.list(Boolean my_subscriptions)
33
+
34
+
@@ -1,4 +1,4 @@
1
1
  module Noggin
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
4
4
 
metadata CHANGED
@@ -1,45 +1,51 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: noggin
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
+ prerelease:
5
6
  platform: ruby
6
7
  authors:
7
8
  - Geoff Youngs
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2013-09-09 00:00:00.000000000 Z
12
+ date: 2013-09-21 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: rubber-generate
15
16
  requirement: !ruby/object:Gem::Requirement
17
+ none: false
16
18
  requirements:
17
- - - '>='
19
+ - - ! '>='
18
20
  - !ruby/object:Gem::Version
19
21
  version: 0.0.21
20
22
  type: :runtime
21
23
  prerelease: false
22
24
  version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
23
26
  requirements:
24
- - - '>='
27
+ - - ! '>='
25
28
  - !ruby/object:Gem::Version
26
29
  version: 0.0.21
27
30
  - !ruby/object:Gem::Dependency
28
31
  name: prawn
29
32
  requirement: !ruby/object:Gem::Requirement
33
+ none: false
30
34
  requirements:
31
- - - '>='
35
+ - - ! '>='
32
36
  - !ruby/object:Gem::Version
33
37
  version: 0.0.21
34
38
  type: :development
35
39
  prerelease: false
36
40
  version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
37
42
  requirements:
38
- - - '>='
43
+ - - ! '>='
39
44
  - !ruby/object:Gem::Version
40
45
  version: 0.0.21
41
- description: |
42
- smb
46
+ description: ! 'smb
47
+
48
+ '
43
49
  email: git@intersect-uk.co.uk
44
50
  executables: []
45
51
  extensions:
@@ -57,25 +63,29 @@ files:
57
63
  homepage: http://github.com/geoffyoungs/noggin
58
64
  licenses:
59
65
  - MIT
60
- metadata: {}
61
66
  post_install_message:
62
67
  rdoc_options: []
63
68
  require_paths:
64
69
  - lib
65
70
  required_ruby_version: !ruby/object:Gem::Requirement
71
+ none: false
66
72
  requirements:
67
- - - '>='
73
+ - - ! '>='
68
74
  - !ruby/object:Gem::Version
69
75
  version: '0'
76
+ segments:
77
+ - 0
78
+ hash: 211198722336226809
70
79
  required_rubygems_version: !ruby/object:Gem::Requirement
80
+ none: false
71
81
  requirements:
72
- - - '>='
82
+ - - ! '>='
73
83
  - !ruby/object:Gem::Version
74
84
  version: '0'
75
85
  requirements: []
76
86
  rubyforge_project:
77
- rubygems_version: 2.0.3
87
+ rubygems_version: 1.8.25
78
88
  signing_key:
79
- specification_version: 4
89
+ specification_version: 3
80
90
  summary: libcups bindings for ruby
81
91
  test_files: []
checksums.yaml DELETED
@@ -1,7 +0,0 @@
1
- ---
2
- SHA1:
3
- metadata.gz: 4f3c0bc5a5a640b5822bf85970579af604d006d3
4
- data.tar.gz: d47a8a20702b937c304b1b7af1fc6aa32c89fbe3
5
- SHA512:
6
- metadata.gz: c2474bab572fff36b7f0e7566d8dfad018df21ae0aec337fc02be0c742dda55eab029f80953c0e4ed739bfd0cd20305be3b17d3e8ea891ecad132bc6fd0d70d9
7
- data.tar.gz: dac26f05f9844e3763d55b9d917a219c064e13495e0686db719c63ae530a1892084fecf826fb67df3cf973c1ceadc0533af1b76715a7256ad0e96bc6590e5549