Kannel: Open Source WAP and SMS gateway  $Revision: 5037 $
wtp_init.c
Go to the documentation of this file.
1 /* ====================================================================
2  * The Kannel Software License, Version 1.0
3  *
4  * Copyright (c) 2001-2016 Kannel Group
5  * Copyright (c) 1998-2001 WapIT Ltd.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in
17  * the documentation and/or other materials provided with the
18  * distribution.
19  *
20  * 3. The end-user documentation included with the redistribution,
21  * if any, must include the following acknowledgment:
22  * "This product includes software developed by the
23  * Kannel Group (http://www.kannel.org/)."
24  * Alternately, this acknowledgment may appear in the software itself,
25  * if and wherever such third-party acknowledgments normally appear.
26  *
27  * 4. The names "Kannel" and "Kannel Group" must not be used to
28  * endorse or promote products derived from this software without
29  * prior written permission. For written permission, please
30  * contact org@kannel.org.
31  *
32  * 5. Products derived from this software may not be called "Kannel",
33  * nor may "Kannel" appear in their name, without prior written
34  * permission of the Kannel Group.
35  *
36  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  * DISCLAIMED. IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS
40  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
41  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
42  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
43  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
44  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
45  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
46  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Kannel Group. For more information on
51  * the Kannel Group, please see <http://www.kannel.org/>.
52  *
53  * Portions of this software are based upon software originally written at
54  * WapIT Ltd., Helsinki, Finland for the Kannel project.
55  */
56 
57 /*
58  * wtp_init.c - WTP initiator implementation
59  *
60  * By Aarno Syvšnen for Wapit Ltd
61  */
62 
63 #include "gwlib/gwlib.h"
64 #include "wtp_init.h"
65 #include "wtp_pack.h"
66 #include "wap.h"
67 
68 /*****************************************************************************
69  * Internal data structures.
70  *
71  * List of initiator WTP machines
72  */
73 static List *init_machines = NULL;
74 
75 /*
76  * Counter for initiator WTP machine id numbers, to make sure they are unique.
77  */
79 
80 /*
81  * When we restart an iniator, we must set tidnew flag to avoid excessive tid
82  * validations (WTP 8.8.3.2). Only an iniator uses this flag.
83  */
84 static int tidnew = 1;
85 
86 /*
87  * Queue of events to be handled by WTP initiator.
88  */
89 static List *queue = NULL;
90 
91 /*
92  * Give the status of the wtp initiator:
93  *
94  * limbo
95  * not running at all
96  * running
97  * operating normally
98  * terminating
99  * waiting for operations to terminate, returning to limbo
100  */
102 
105 
106 /*
107  * This is a timer 'tick'. All timer values multiplies of this value.
108  */
109 static long init_timer_freq = -1;
110 
111 /***************************************************************************
112  *
113  * Prototypes for internal functions:
114  */
115 static void main_thread(void *arg);
116 
117 /*
118  * Create and destroy an uniniatilised wtp initiator state machine
119  */
120 static WTPInitMachine *init_machine_create(WAPAddrTuple *tuple, unsigned short
121  tid, int tidnew);
122 static void init_machine_destroy(void *sm);
123 static void handle_init_event(WTPInitMachine *machine, WAPEvent *event);
124 
125 /*
126  * Checks whether wtp initiator machines data structure includes a specific
127  * machine.
128  * The machine in question is identified with with source and destination
129  * address and port and tid.
130  */
132 
133 /*
134  * Creates TR-Abort.ind event.
135  */
136 static WAPEvent *create_tr_abort_ind(WTPInitMachine *sm, long abort_reason);
137 
138 /*
139  * Creates TR-Invoke.cnf event
140  */
142 static int tid_wrapped(unsigned short tid);
143 
144 /*
145  * Create a datagram with an Abort PDU and send it to the WDP layer.
146  */
147 static void send_abort(WTPInitMachine *machine, long type, long reason);
148 
149 /*
150  * Create a datagram with an Ack PDU and send it to the WDP layer.
151  */
152 static void send_ack(WTPInitMachine *machine, long ack_type, int rid_flag);
153 
154 /*
155  * We use RcvTID consistently as a internal tid representation. So newly
156  * created tids are converted. SendTID = RcvTID ^ 0x8000 (WTP 10.4.3) and for
157  * an initiator, GenTID = SendTID (WTP 10.5).
158  */
159 static unsigned short rcv_tid(unsigned short tid);
160 static void start_initiator_timer_R(WTPInitMachine *machine);
161 static void stop_initiator_timer(Timer *timer);
162 
163 /**************************************************************************
164  *
165  * EXTERNAL FUNCTIONS
166  */
167 
168 void wtp_initiator_init(wap_dispatch_func_t *datagram_dispatch,
169  wap_dispatch_func_t *session_dispatch, long timer_freq)
170 {
171  init_machines = gwlist_create();
172  init_machine_id_counter = counter_create();
173 
174  queue = gwlist_create();
175  gwlist_add_producer(queue);
176 
177  dispatch_to_wdp = datagram_dispatch;
178  dispatch_to_wsp = session_dispatch;
179 
180  timers_init();
182 
186 }
187 
189 {
192  gwlist_remove_producer(queue);
194 
195  debug("wap.wtp", 0, "wtp_initiator_shutdown: %ld init_machines left",
196  gwlist_len(init_machines));
197  gwlist_destroy(init_machines, init_machine_destroy);
199 
200  counter_destroy(init_machine_id_counter);
201  timers_shutdown();
202 }
203 
205 {
206  gwlist_produce(queue, event);
207 }
208 
209 /**************************************************************************
210  *
211  * INTERNAL FUNCTIONS:
212  */
213 
214 static void main_thread(void *arg)
215 {
216  WTPInitMachine *sm;
217  WAPEvent *e;
218 
219  while (initiator_run_status == running &&
220  (e = gwlist_consume(queue)) != NULL) {
222  if (sm == NULL)
224  else
225  handle_init_event(sm, e);
226  }
227 }
228 
230  tid, int tidnew)
231 {
232  WTPInitMachine *init_machine;
233 
234  init_machine = gw_malloc(sizeof(WTPInitMachine));
235 
236  #define ENUM(name) init_machine->name = INITIATOR_NULL_STATE;
237  #define INTEGER(name) init_machine->name = 0;
238  #define EVENT(name) init_machine->name = NULL;
239  #define TIMER(name) init_machine->name = gwtimer_create(queue);
240  #define ADDRTUPLE(name) init_machine->name = NULL;
241  #define MACHINE(field) field
242  #include "wtp_init_machine.def"
243 
244  gwlist_append(init_machines, init_machine);
245 
246  init_machine->mid = counter_increase(init_machine_id_counter);
247  init_machine->addr_tuple = wap_addr_tuple_duplicate(tuple);
248  init_machine->tid = tid;
249  init_machine->tidnew = tidnew;
250 
251  debug("wap.wtp", 0, "WTP: Created WTPInitMachine %p (%ld)",
252  (void *) init_machine, init_machine->mid);
253 
254  return init_machine;
255 }
256 
257 /*
258  * Destroys a WTPInitMachine. Assumes it is safe to do so. Assumes it has
259  * already been deleted from the machines list.
260  */
261 static void init_machine_destroy(void *p)
262 {
263  WTPInitMachine *init_machine;
264 
265  init_machine = p;
266  debug("wap.wtp", 0, "WTP: Destroying WTPInitMachine %p (%ld)",
267  (void *) init_machine, init_machine->mid);
268 
269  gwlist_delete_equal(init_machines, init_machine);
270 
271  #define ENUM(name) init_machine->name = INITIATOR_NULL_STATE;
272  #define INTEGER(name) init_machine->name = 0;
273  #define EVENT(name) wap_event_destroy(init_machine->name);
274  #define TIMER(name) gwtimer_destroy(init_machine->name);
275  #define ADDRTUPLE(name) wap_addr_tuple_destroy(init_machine->name);
276  #define MACHINE(field) field
277  #include "wtp_init_machine.def"
278  gw_free(init_machine);
279 }
280 
281 /*
282  * Give the name of an initiator state in a readable form.
283  */
284 static unsigned char *name_init_state(int s)
285 {
286  switch (s){
287  #define INIT_STATE_NAME(state) case state: return (unsigned char *) #state;
288  #define ROW(state, event, condition, action, new_state)
289  #include "wtp_init_states.def"
290  default:
291  return (unsigned char *)"unknown state";
292  }
293 }
294 
295 /*
296  * Feed an event to a WTP initiator state machine. Handle all errors by do not
297  * report them to the caller. WSP indication or conformation is handled by an
298  * included state table. Note: Do not put {}s of the else block inside the
299  * macro definition .
300  */
301 static void handle_init_event(WTPInitMachine *init_machine, WAPEvent *event)
302 {
303  WAPEvent *wsp_event = NULL;
304 
305  debug("wap.wtp", 0, "WTP_INIT: initiator machine %ld, state %s,"
306  " event %s.",
307  init_machine->mid,
308  name_init_state(init_machine->state),
309  wap_event_name(event->type));
310 
311  #define INIT_STATE_NAME(state)
312  #define ROW(init_state, event_type, condition, action, next_state) \
313  if (init_machine->state == init_state && \
314  event->type == event_type && \
315  (condition)) { \
316  action \
317  init_machine->state = next_state; \
318  debug("wap.wtp", 0, "WTP_INIT %ld: New state %s", \
319  init_machine->mid, #next_state); \
320  } else
321  #include "wtp_init_states.def"
322  {
323  error(1, "WTP_INIT: handle_init_event: unhandled event!");
324  debug("wap.wtp.init", 0, "WTP_INIT: handle_init_event:"
325  "Unhandled event was:");
326  wap_event_dump(event);
327  wap_event_destroy(event);
328  return;
329  }
330 
331  if (event != NULL) {
332  wap_event_destroy(event);
333  }
334 
335  if (init_machine->state == INITIATOR_NULL_STATE)
336  init_machine_destroy(init_machine);
337 }
338 
339 static int is_wanted_init_machine(void *a, void *b)
340 {
341  struct machine_pattern *pat;
342  WTPInitMachine *m;
343 
344  m = a;
345  pat = b;
346 
347  if (m->mid == pat->mid)
348  return 1;
349 
350  if (pat->mid != -1)
351  return 0;
352 
353  return m->tid == pat->tid &&
354  wap_addr_tuple_same(m->addr_tuple, pat->tuple);
355 }
356 
358  long mid)
359 {
360  struct machine_pattern pat;
361  WTPInitMachine *m;
362 
363  pat.tuple = tuple;
364  pat.tid = tid;
365  pat.mid = mid;
366 
367  m = gwlist_search(init_machines, &pat, is_wanted_init_machine);
368  return m;
369 }
370 
371 /*
372  * Checks whether wtp initiator machines data structure includes a specific
373  * machine. The machine in question is identified with with source and
374  * destination address and port and tid. First test incoming events
375  * (WTP 10.2) (Exception are tests nro 4 and 5: if we have a memory error,
376  * we panic (nro 4); nro 5 is already checked). If we have an ack with tid
377  * verification flag set and no corresponding transaction, we abort.(case nro
378  * 2). If the event was a normal ack or an abort, it is ignored (error nro 3).
379  * In the case of TR-Invoke.req a new machine is created, in the case of
380  * TR-Abort.req we have a serious error. We must create a new tid for a new
381  * transaction here, because machines are identified by an address tuple and a
382  * tid. This tid is GenTID (WTP 10.4.2), which is used only by the wtp iniator
383  * thread.
384  * Note that as internal tid representation, module uses RcvTID (as required
385  * by module wtp_pack). So we we turn the first bit of the tid stored by the
386  * init machine.
387  */
389 {
390  WTPInitMachine *machine = NULL;
391  long mid;
392  static long tid = -1;
394 
395  mid = -1;
396  tuple = NULL;
397 
398  switch (event->type) {
399  case RcvAck:
400  tid = event->u.RcvAck.tid;
401  tuple = event->u.RcvAck.addr_tuple;
402  break;
403 
404  case RcvAbort:
405  tid = event->u.RcvAbort.tid;
406  tuple = event->u.RcvAbort.addr_tuple;
407  break;
408 
409  case RcvErrorPDU:
410  mid = event->u.RcvErrorPDU.tid;
411  tid = event->u.RcvErrorPDU.tid;
412  tuple = event->u.RcvErrorPDU.addr_tuple;
413  break;
414 /*
415  * When we are receiving an invoke requirement, we must create a new trans-
416  * action and generate a new tid. This can be wrapped, and should have its
417  * first bit turned.
418  */
419  case TR_Invoke_Req:
420  ++tid;
421  if (tid_wrapped(tid)) {
422  tidnew = 1;
423  tid = 0;
424  }
425 
426  tid = rcv_tid(tid);
427  tuple = event->u.TR_Invoke_Req.addr_tuple;
428  mid = event->u.TR_Invoke_Req.handle;
429  break;
430 
431  case TR_Abort_Req:
432  tid = event->u.TR_Abort_Req.handle;
433  break;
434 
435  case TimerTO_R:
436  mid = event->u.TimerTO_R.handle;
437  break;
438 
439  default:
440  error(0, "WTP_INIT: machine_find_or_create: unhandled event");
441  wap_event_dump(event);
442  return NULL;
443  }
444 
445  gw_assert(tuple != NULL || mid != -1);
446  machine = init_machine_find(tuple, tid, mid);
447 
448  if (machine == NULL){
449 
450  switch (event->type){
451  case RcvAck:
452 
453 /*
454  * Case nro 2 If we do not have a tid asked for, we send a negative answer,
455  * i.e. an abort with reason INVALIDTID.
456  */
457  if (event->u.RcvAck.tid_ok) {
459  tid, tuple));
460  }
461 
462 /* Case nro 3, normal ack */
463  else
464  info(0, "WTP_INIT: machine_find_or_create: ack "
465  "received, yet having no machine");
466  break;
467 
468 /* Case nro 3, abort */
469  case RcvAbort:
470  info(0, "WTP_INIT: machine_find_or_create: abort "
471  "received, yet having no machine");
472  break;
473 
474  case TR_Invoke_Req:
475  machine = init_machine_create(tuple, tid, tidnew);
476  machine->mid = event->u.TR_Invoke_Req.handle;
477  break;
478 
479  case TR_Abort_Req:
480  error(0, "WTP_INIT: machine_find_or_create: WSP "
481  "primitive to a wrong WTP machine");
482  break;
483 
484  case TimerTO_R:
485  error(0, "WTP_INIT: machine_find_or_create: timer "
486  "event without a corresponding machine");
487  break;
488 
489  default:
490  error(0, "WTP_INIT: machine_find_or_create: unhandled"
491  "event");
492  wap_event_dump(event);
493  break;
494  }
495  }
496 
497  return machine;
498 }
499 
500 /*
501  * Creates TR-Invoke.cnf event
502  */
504 {
505  WAPEvent *event;
506 
507  gw_assert(init_machine != NULL);
508  event = wap_event_create(TR_Invoke_Cnf);
509  event->u.TR_Invoke_Cnf.handle = init_machine->mid;
510  event->u.TR_Invoke_Cnf.addr_tuple =
511  wap_addr_tuple_duplicate(init_machine->addr_tuple);
512 
513  return event;
514 }
515 
516 /*
517  * Creates TR-Abort.ind event from an initiator state machine. In addtion, set
518  * the ir_flag on.
519  */
520 static WAPEvent *create_tr_abort_ind(WTPInitMachine *sm, long abort_reason)
521 {
522  WAPEvent *event;
523 
524  event = wap_event_create(TR_Abort_Ind);
525 
526  event->u.TR_Abort_Ind.abort_code = abort_reason;
527  event->u.TR_Abort_Ind.addr_tuple =
528  wap_addr_tuple_duplicate(sm->addr_tuple);
529  event->u.TR_Abort_Ind.handle = sm->mid;
530  event->u.TR_Abort_Ind.ir_flag = INITIATOR_INDICATION;
531 
532  return event;
533 }
534 
535 
536 static int tid_wrapped(unsigned short tid)
537 {
538  return tid > (1 << 15);
539 }
540 
541 static unsigned short rcv_tid(unsigned short tid)
542 {
543  return tid ^ 0x8000;
544 }
545 
546 /*
547  * Start retry interval timer (strictly speaking, timer iniatilised with retry
548  * interval). Multiply timer value with init_timer_freq.
549  */
551 {
552  WAPEvent *timer_event;
553  int seconds;
554 
555  timer_event = wap_event_create(TimerTO_R);
556  timer_event->u.TimerTO_R.handle = machine->mid;
557  if (machine->u_ack)
559  else
561  gwtimer_start(machine->timer, seconds, timer_event);
562 }
563 
564 static void stop_initiator_timer(Timer *timer)
565 {
566  debug("wap.wtp_init", 0, "stopping timer");
567  gw_assert(timer);
568  gwtimer_stop(timer);
569 }
570 
571 static void send_abort(WTPInitMachine *machine, long type, long reason)
572 {
573  WAPEvent *e;
574 
575  e = wtp_pack_abort(type, reason, machine->tid, machine->addr_tuple);
576  dispatch_to_wdp(e);
577 }
578 
579 static void send_ack(WTPInitMachine *machine, long ack_type, int rid_flag)
580 {
581  WAPEvent *e;
582 
583  e = wtp_pack_ack(ack_type, rid_flag, machine->tid, machine->addr_tuple);
584  dispatch_to_wdp(e);
585 }
static int is_wanted_init_machine(void *a, void *b)
Definition: wtp_init.c:339
void error(int err, const char *fmt,...)
Definition: log.c:612
void info(int err, const char *fmt,...)
Definition: log.c:636
static int tidnew
Definition: wtp_init.c:84
void * gwlist_search(List *list, void *pattern, int(*cmp)(void *, void *))
Definition: list.c:486
static void stop_initiator_timer(Timer *timer)
Definition: wtp_init.c:564
static void send_abort(WTPInitMachine *machine, long type, long reason)
Definition: wtp_init.c:571
void counter_destroy(Counter *counter)
Definition: counter.c:110
void gwlist_append(List *list, void *item)
Definition: list.c:179
long tid
Definition: wtp.h:190
static unsigned short rcv_tid(unsigned short tid)
Definition: wtp_init.c:541
void gwlist_produce(List *list, void *item)
Definition: list.c:411
long gwlist_len(List *list)
Definition: list.c:166
static void handle_init_event(WTPInitMachine *machine, WAPEvent *event)
Definition: wtp_init.c:301
static void start_initiator_timer_R(WTPInitMachine *machine)
Definition: wtp_init.c:550
static List * init_machines
Definition: wtp_init.c:73
int type
Definition: smsc_cimd2.c:215
static int tid_wrapped(unsigned short tid)
Definition: wtp_init.c:536
void gwtimer_stop(Timer *timer)
Definition: timers.c:299
static WAPEvent * create_tr_abort_ind(WTPInitMachine *sm, long abort_reason)
Definition: wtp_init.c:520
void wap_event_dump(WAPEvent *event)
Definition: wap_events.c:181
void gwthread_join_every(gwthread_func_t *func)
unsigned long counter_increase(Counter *counter)
Definition: counter.c:123
WAPAddrTuple * wap_addr_tuple_duplicate(WAPAddrTuple *tuple)
Definition: wap_addr.c:125
WAPAddrTuple * tuple
Definition: wtp.h:189
unsigned long mid
Definition: wtp_init.h:90
T DUnitdata TR Invoke TR Invoke TR Result TR Abort S Connect S Suspend S Resume S Suspend S Resume S Disconnect S MethodInvoke S MethodInvoke S MethodResult S MethodInvoke S MethodResult S MethodAbort S Push S ConfirmedPush S ConfirmedPush S PushAbort RcvAck
Definition: wap_events.h:532
void wtp_initiator_dispatch_event(WAPEvent *event)
Definition: wtp_init.c:204
static wap_dispatch_func_t * dispatch_to_wsp
Definition: wtp_init.c:104
Counter * counter_create(void)
Definition: counter.c:94
static unsigned char * name_init_state(int s)
Definition: wtp_init.c:284
static enum @104 initiator_run_status
static void init_machine_destroy(void *sm)
Definition: wtp_init.c:261
void gwtimer_start(Timer *timer, int interval, WAPEvent *event)
Definition: timers.c:254
void gwlist_remove_producer(List *list)
Definition: list.c:401
void wtp_initiator_init(wap_dispatch_func_t *datagram_dispatch, wap_dispatch_func_t *session_dispatch, long timer_freq)
Definition: wtp_init.c:168
static WTPInitMachine * init_machine_find(WAPAddrTuple *tuple, long tid, long mid)
Definition: wtp_init.c:357
void wap_event_destroy_item(void *event)
Definition: wap_events.c:130
static WAPEvent * create_tr_invoke_cnf(WTPInitMachine *machine)
Definition: wtp_init.c:503
static void main_thread(void *arg)
Definition: wtp_init.c:214
const char * wap_event_name(WAPEventName type)
Definition: wap_events.c:169
long gwlist_delete_equal(List *list, void *item)
Definition: list.c:266
long mid
Definition: wtp.h:191
#define wap_event_create(type)
Definition: wap_events.h:107
Definition: wtp.h:124
void timers_shutdown(void)
Definition: timers.c:196
static Counter * init_machine_id_counter
Definition: wtp_init.c:78
#define gwthread_create(func, arg)
Definition: gwthread.h:90
gw_assert(wtls_machine->packet_to_send!=NULL)
void timers_init(void)
Definition: timers.c:184
WAPEvent * wtp_pack_ack(long ack_type, int rid_flag, long tid, WAPAddrTuple *address)
Definition: wtp_pack.c:243
static WTPInitMachine * init_machine_find_or_create(WAPEvent *event)
Definition: wtp_init.c:388
int wap_addr_tuple_same(WAPAddrTuple *a, WAPAddrTuple *b)
Definition: wap_addr.c:118
static WTPInitMachine * init_machine_create(WAPAddrTuple *tuple, unsigned short tid, int tidnew)
Definition: wtp_init.c:229
void * gwlist_consume(List *list)
Definition: list.c:427
void debug(const char *place, int err, const char *fmt,...)
Definition: log.c:690
WAPEventName type
Definition: wap_events.h:88
WAPEvent * wtp_pack_abort(long abort_type, long abort_reason, long tid, WAPAddrTuple *address)
Definition: wtp_pack.c:223
#define gwlist_create()
Definition: list.h:136
static List * queue
Definition: wtp_init.c:89
void wtp_initiator_shutdown(void)
Definition: wtp_init.c:188
static wap_dispatch_func_t * dispatch_to_wdp
Definition: wtp_init.c:103
static long init_timer_freq
Definition: wtp_init.c:109
void gwlist_add_producer(List *list)
Definition: list.c:383
union WAPEvent::@87 u
Definition: list.c:102
static void send_ack(WTPInitMachine *machine, long ack_type, int rid_flag)
Definition: wtp_init.c:579
void wap_event_destroy(WAPEvent *event)
Definition: wap_events.c:102
void wap_dispatch_func_t(WAPEvent *event)
Definition: wap.h:85
static long timer_freq
Definition: wapbox.c:104
void gwlist_destroy(List *list, gwlist_item_destructor_t *destructor)
Definition: list.c:145
See file LICENSE for details about the license agreement for using, modifying, copying or deriving work from this software.