View | Details | Raw Unified | Return to bug 14767
Collapse All | Expand All

(-)a/C4/Installer/PerlDependencies.pm (+5 lines)
Lines 204-209 our $PERL_DEPS = { Link Here
204
        'required' => '1',
204
        'required' => '1',
205
        'min_ver'  => '2.033'
205
        'min_ver'  => '2.033'
206
    },
206
    },
207
    'LWP::Curl' => {
208
        'usage'    => 'Curl',
209
        'required' => '0',
210
        'min_ver'  => '0.12',
211
    },
207
    'MIME::Base64' => {
212
    'MIME::Base64' => {
208
        'usage'    => 'Core',
213
        'usage'    => 'Core',
209
        'required' => '1',
214
        'required' => '1',
(-)a/C4/Letters.pm (+7 lines)
Lines 1442-1447 sub _send_message_by_sms { Link Here
1442
    try {
1442
    try {
1443
        $success = C4::SMS->send_sms( { destination => $member->{'smsalertnumber'},
1443
        $success = C4::SMS->send_sms( { destination => $member->{'smsalertnumber'},
1444
                                           message     => $message->{'content'},
1444
                                           message     => $message->{'content'},
1445
                                           message_id  => $message->{'message_id'},
1445
                                         } );
1446
                                         } );
1446
        _set_message_status( { message_id => $message->{'message_id'},
1447
        _set_message_status( { message_id => $message->{'message_id'},
1447
                               status     => ($success ? 'sent' : 'failed'),
1448
                               status     => ($success ? 'sent' : 'failed'),
Lines 1456-1461 sub _send_message_by_sms { Link Here
1456
                                        status     => 'pending',
1457
                                        status     => 'pending',
1457
                                        delivery_note => 'Connection failed. Attempting to resend.' } );
1458
                                        delivery_note => 'Connection failed. Attempting to resend.' } );
1458
            }
1459
            }
1460
            elsif ($_->isa('Koha::Exception::SMSDeliveryFailure')){
1461
                # SMS delivery was unsuccessful. Set message to failed and give a delivery note
1462
                _set_message_status ( { message_id => $message->{'message_id'},
1463
                                        status     => 'failed',
1464
                                        delivery_note => $_->error } );
1465
            }
1459
            else {
1466
            else {
1460
                $_->rethrow();
1467
                $_->rethrow();
1461
            }
1468
            }
(-)a/C4/SMS.pm (+1 lines)
Lines 92-97 sub send_sms { Link Here
92
        # Send a message
92
        # Send a message
93
        $sent = $sender->send_sms( to   => $params->{'destination'},
93
        $sent = $sender->send_sms( to   => $params->{'destination'},
94
                                  text => $params->{'message'},
94
                                  text => $params->{'message'},
95
                                  _message_id => $params->{'message_id'},
95
                             );
96
                             );
96
        return $sent;
97
        return $sent;
97
    } catch {
98
    } catch {
(-)a/Koha/Exception/SMSDeliveryFailure.pm (+28 lines)
Line 0 Link Here
1
package Koha::Exception::SMSDeliveryFailure;
2
3
# Copyright 2015 Vaara-kirjastot
4
#
5
# This file is part of Koha.
6
#
7
# Koha is free software; you can redistribute it and/or modify it under the
8
# terms of the GNU General Public License as published by the Free Software
9
# Foundation; either version 2 of the License, or (at your option) any later
10
# version.
11
#
12
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License along
17
# with Koha; if not, write to the Free Software Foundation, Inc.,
18
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20
use Modern::Perl;
21
22
use Exception::Class (
23
    'Koha::Exception::SMSDeliveryFailure' => {
24
        description => 'SMS delivery was failed.',
25
    },
26
);
27
28
return 1;
(-)a/Koha/REST/V1/Messages/Reports.pm (+37 lines)
Line 0 Link Here
1
package Koha::REST::V1::Messages::Reports;
2
3
use Modern::Perl;
4
5
use Mojo::Base 'Mojolicious::Controller';
6
7
use C4::Letters;
8
use Data::Dumper;
9
10
11
12
=head2 list_messages($c, $args, $cb)
13
14
Lists all messages from message_queue.
15
16
=cut
17
sub create_labyrintti_report {
18
    my ($c, $args, $cb) = @_;
19
20
    my $message = C4::Letters::GetMessage($args->{'messagenumber'});
21
22
    return $c->$cb({ error => "No messages found." }, 404) if not $message;
23
    
24
    # delivery was failed. edit status to failed and add delivery note
25
    if ($args->{'status'} eq "ERROR") {
26
        C4::Letters::UpdateQueuedMessage({
27
                            message_id      => $message->{message_id},
28
                            status          => 'failed',
29
                            delivery_note   => $args->{message},
30
                    });
31
        return $c->$cb('', 201);
32
    }
33
    
34
    return $c->$cb('', 204);
35
}
36
37
1;
(-)a/SMS/Send/Labyrintti/Driver.pm (+129 lines)
Line 0 Link Here
1
=head IN THIS FILE
2
3
This module extends the SMS::Send::Driver interface
4
to implement a driver compatible with the Labyrintti SMS Gateway HTTP interface.
5
6
Module parameters are sanitated against injection attacks.
7
8
Labyrintti responds:
9
10
    success
11
12
phone-number OK message-count description
13
+358401234567 OK 1 message accepted for sending
14
15
    failure
16
17
phone-number ERROR error-code message-count description
18
e.g: 12345 ERROR 2 1 message failed: Too short phone number
19
20
=cut
21
22
package SMS::Send::Labyrintti::Driver;
23
#use Modern::Perl; #Can't use this since SMS::Send uses hash keys starting with _
24
use utf8;
25
use SMS::Send::Driver ();
26
use LWP::Curl;
27
use LWP::UserAgent;
28
use URI::Escape;
29
use C4::Context;
30
use Encode;
31
use Koha::Exception::ConnectionFailed;
32
use Koha::Exception::SMSDeliveryFailure;
33
34
use vars qw{$VERSION @ISA};
35
BEGIN {
36
        $VERSION = '0.06';
37
                @ISA     = 'SMS::Send::Driver';
38
}
39
40
41
#####################################################################
42
# Constructor
43
44
sub new {
45
       my $class = shift;
46
        my $params = {@_};
47
        my $username = $params->{_login} ? $params->{_login} : C4::Context->config('smsProviders')->{'labyrintti'}->{'user'};
48
        my $password = $params->{_password} ? $params->{_password} : C4::Context->config('smsProviders')->{'labyrintti'}->{'passwd'};
49
50
        if (! defined $username ) {
51
            warn "->send_sms(_login) must be defined!";
52
            return;
53
        }
54
        if (! defined $password ) {
55
            warn "->send_sms(_password) must be defined!";
56
            return;
57
        }
58
59
        #Prevent injection attack    
60
        $self->{_login} =~ s/'//g;
61
        $self->{_password} =~ s/'//g;
62
63
        # Create the object
64
        my $self = bless {}, $class;
65
66
        $self->{UserAgent} = LWP::UserAgent->new(timeout => 5);
67
        $self->{_login} = $username;
68
        $self->{_password} = $password;
69
70
        return $self;
71
}
72
73
sub send_sms {
74
    my $self    = shift;
75
    my $params = {@_};
76
    my $message = $params->{text};
77
    my $recipientNumber = $params->{to};
78
79
    if (! defined $message ) {
80
        warn "->send_sms(text) must be defined!";
81
        return;
82
    }
83
    if (! defined $recipientNumber ) {
84
        warn "->send_sms(to) must be defined!";
85
        return;
86
    }
87
88
    #Prevent injection attack!
89
    $recipientNumber =~ s/'//g;
90
    $message =~ s/(")|(\$\()|(`)/\\"/g; #Sanitate " so it won't break the system( iconv'ed curl command )
91
92
    
93
    
94
    my $base_url = "https://gw.labyrintti.com:28443/sendsms";
95
    my $parameters = {
96
        'user'      => $self->{_login},
97
        'password'  => $self->{_password},
98
        'dests'     => $recipientNumber,
99
        'text'      => $message,
100
        'unicode'   => 'yes',
101
    };
102
    
103
    my $report_url = C4::Context->config('smsProviders')->{'labyrintti'}->{'reportUrl'};
104
    if ($report_url) {
105
        my $msg_id = $params->{_message_id};
106
        $report_url =~ s/\{messagenumber\}/$msg_id/g;
107
        $parameters->{'report'} = $report_url;
108
    }
109
    
110
    my $lwpcurl = LWP::Curl->new(auto_encode => 0);
111
    my $return = $lwpcurl->post($base_url, $parameters);
112
113
    if ($lwpcurl->{retcode} == 6) {
114
        Koha::Exception::ConnectionFailed->throw(error => "Connection failed");
115
    }
116
117
    my $delivery_note = $return;
118
119
    return 1 if ($return =~ m/OK [1-9](\d*)?/);
120
121
    # remove everything except the delivery note
122
    $delivery_note =~ s/^(.*)message\sfailed:\s*//g;
123
    
124
    # pass on the error by throwing an exception - it will be eventually caught
125
    # in C4::Letters::_send_message_by_sms()
126
    Koha::Exception::SMSDeliveryFailure->throw(error => $delivery_note);    
127
}
128
129
1;
(-)a/api/v1/swagger.json (+50 lines)
Lines 507-512 Link Here
507
          }
507
          }
508
        }
508
        }
509
      }
509
      }
510
    },
511
    "/messages/{messagenumber}/report/labyrintti": {
512
      "post": {
513
        "x-mojo-controller": "Koha::REST::V1::Messages::Reports",
514
        "operationId": "createLabyrinttiReport",
515
        "tags": ["messages"],
516
        "parameters": [
517
          {
518
            "$ref": "#/parameters/messagenumberPathParam"
519
          },
520
          {
521
            "name": "status",
522
            "in": "formData",
523
            "type": "string",
524
            "description": "Status of the delivery",
525
            "required": true
526
          },
527
          {
528
            "name": "message",
529
            "in": "formData",
530
            "type": "string",
531
            "description": "Delivery notes",
532
            "required": true
533
          }
534
        ],
535
        "consumes": ["application/x-www-form-urlencoded"],
536
        "produces": [
537
          "application/json"
538
        ],
539
        "responses": {
540
          "201": {
541
            "description": "Response for creating a new report. Required status ERROR",
542
            "schema": {
543
              "type": "string"
544
            }
545
          },
546
          "204": {
547
            "description": "Response for receiving delivery report with status OK",
548
            "schema": {
549
              "type": "string"
550
            }
551
          },
552
          "404": {
553
            "description": "Message not found",
554
            "schema": {
555
              "$ref": "#/definitions/error"
556
            }
557
          }
558
        }
559
      }
510
    }
560
    }
511
  },
561
  },
512
  "definitions": {
562
  "definitions": {
(-)a/etc/koha-conf.xml (+14 lines)
Lines 145-149 __PAZPAR2_TOGGLE_XML_POST__ Link Here
145
    </rest>
145
    </rest>
146
 </testservers>
146
 </testservers>
147
147
148
 <smsProviders>
149
    <labyrintti>
150
        <!-- Login details for SMS Gateway service --> 
151
        <user></user>
152
        <passwd></passwd>
153
        <!-- Report URL is the page where your SMS Gateway provider will send a report
154
             about the delivery of the message.
155
156
             With REST API, it should be
157
             http://mydomain.com/api/v1/messages/{messagenumber}/report/_your_gateway_
158
                                                                                    -->
159
        <reportUrl></reportUrl>
160
    </labyrintti>
161
 </smsProviders>
148
</config>
162
</config>
149
</yazgfs>
163
</yazgfs>
(-)a/etc/koha-httpd.conf (+18 lines)
Lines 39-44 Link Here
39
   ProxyPassReverse /api/v1/  http://localhost:8081/api/v1/
39
   ProxyPassReverse /api/v1/  http://localhost:8081/api/v1/
40
   ##REVERSE PROXYING OK
40
   ##REVERSE PROXYING OK
41
41
42
# Uncomment the following block to restrict access for changing
43
# message status via reports to only from certain IPs.
44
#   <LocationMatch "/v1/messages/\d+/report">
45
#      Order Deny,Allow
46
#      Deny from all
47
#      Allow from 101.123.213.10
48
#      Allow from 101.123.213.11
49
#   </LocationMatch>
50
42
   <Directory "__OPAC_WWW_DIR__">
51
   <Directory "__OPAC_WWW_DIR__">
43
      Options -Indexes
52
      Options -Indexes
44
   </Directory>
53
   </Directory>
Lines 168-173 Link Here
168
   ProxyPassReverse /api/v1/  http://localhost:8081/api/v1/
177
   ProxyPassReverse /api/v1/  http://localhost:8081/api/v1/
169
   ##REVERSE PROXYING OK
178
   ##REVERSE PROXYING OK
170
179
180
# Uncomment the following block to restrict access for changing
181
# message status via reports to only from certain IPs.
182
#   <LocationMatch "/v1/messages/\d+/report">
183
#      Order Deny,Allow
184
#      Deny from all
185
#      Allow from 101.123.213.10
186
#      Allow from 101.123.213.11
187
#   </LocationMatch>
188
171
   <Directory "__INTRANET_WWW_DIR__">
189
   <Directory "__INTRANET_WWW_DIR__">
172
      Options -Indexes
190
      Options -Indexes
173
   </Directory>
191
   </Directory>
(-)a/t/db_dependent/Api/V1/Messages.pm (-1 / +82 lines)
Lines 255-258 sub delete_n_404 { Link Here
255
    return 1;
255
    return 1;
256
}
256
}
257
257
258
sub post_n_reportlabyrintti404 {
259
    my ($class, $restTest, $driver) = @_;
260
    my $testContext = $restTest->get_testContext();
261
    my $activeUser = $restTest->get_activeBorrower();
262
    my $path = $restTest->get_routePath();
263
264
    $path =~ s/\{messagenumber\}/12334893298007/;
265
    $driver->post_ok(
266
                    $path => { Accept => 'application/x-www-form-urlencoded'} =>
267
                    form => {
268
                        status => "OK",
269
                        message => "Message delivery succesful",
270
                    });
271
    $driver->status_is(404);
272
273
    return 1;
274
}
275
276
sub post_n_reportlabyrintti204 {
277
    my ($class, $restTest, $driver) = @_;
278
    my $testContext = $restTest->get_testContext();
279
    my $activeUser = $restTest->get_activeBorrower();
280
    my $path = $restTest->get_routePath();
281
282
    #Create the test context.
283
    my $messages = t::lib::TestObjects::MessageQueueFactory->createTestGroup(
284
                        {
285
                            subject => "The quick brown fox",
286
                            content => "Jumps over the lazy dog.",
287
                            cardnumber => $activeUser->{'_result'}->{'_column_data'}->{cardnumber},
288
                            message_transport_type => 'sms',
289
                            from_address => '11A001@example.com',
290
                        }, undef, $testContext, undef, undef);
291
292
    my $messagenumber = $messages->{'11A001@example.com'}->{message_id};
293
    $path =~ s/\{messagenumber\}/$messagenumber/;
294
    $driver->post_ok(
295
                    $path => { Accept => 'application/x-www-form-urlencoded'} =>
296
                    form => {
297
                        status => "OK",
298
                        message => "Message delivery succesful",
299
                    }
300
            );
301
    $driver->status_is(204);
302
303
    t::lib::TestObjects::ObjectFactory->tearDownTestContext($testContext);
304
    return 1;
305
}
306
307
sub post_n_reportlabyrintti201 {
308
    my ($class, $restTest, $driver) = @_;
309
    my $testContext = $restTest->get_testContext();
310
    my $activeUser = $restTest->get_activeBorrower();
311
    my $path = $restTest->get_routePath();
312
313
    #Create the test context.
314
    my $messages = t::lib::TestObjects::MessageQueueFactory->createTestGroup(
315
                        {
316
                            subject => "The quick brown fox",
317
                            content => "Jumps over the lazy dog.",
318
                            cardnumber => $activeUser->{'_result'}->{'_column_data'}->{cardnumber},
319
                            message_transport_type => 'sms',
320
                            from_address => '11A001@example.com',
321
                        }, undef, $testContext, undef, undef);
322
323
    my $messagenumber = $messages->{'11A001@example.com'}->{message_id};
324
    $path =~ s/\{messagenumber\}/$messagenumber/;
325
    $driver->post_ok(
326
                    $path => { Accept => 'application/x-www-form-urlencoded'} =>
327
                    form => {
328
                        status => "ERROR",
329
                        message => "Test error delivery note",
330
                    }
331
            );
332
    $driver->status_is(201);
333
    my $message = C4::Letters::GetMessage($messagenumber);
334
    is($message->{delivery_note}, "Test error delivery note", "Delivery note for failed SMS received successfully.");
335
336
    t::lib::TestObjects::ObjectFactory->tearDownTestContext($testContext);
337
    return 1;
338
}
339
258
1;
340
1;
259
- 

Return to bug 14767