noggin 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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