├── .gitignore ├── .gitmodules ├── INCOMPATIBLE_CHANGES.txt ├── LICENSE ├── Makefile ├── README.md ├── doc ├── NOTES.txt ├── amisms_example.conf └── chan_dongle_issue1.patch ├── pgsql ├── .gitignore ├── Makefile ├── pgsql.c ├── pgsql.h └── smsd-pgsql.c ├── src ├── Makefile ├── NOTES.txt ├── conf.c ├── conf.h ├── defaults.h ├── option.c ├── option.h ├── pdu │ ├── .gitignore │ ├── Makefile │ ├── charset.c │ ├── charset.h │ ├── compat.c │ ├── compat.h │ ├── pdu.c │ ├── pdu.h │ └── test.c ├── phone_number_validator.c ├── pnv.c ├── pnv.h ├── pnv_json.c ├── sms.c ├── sms.h ├── verbose.c └── verbose.h ├── t ├── .gitignore ├── Makefile ├── check_output.sh ├── test02 │ ├── Makefile │ ├── README │ ├── data │ │ └── test.ini │ ├── output.ok │ └── test.c ├── test03 │ ├── Makefile │ ├── README │ ├── data │ │ └── test.ini │ ├── output.ok │ └── test.c ├── test04 │ ├── Makefile │ ├── README │ ├── data │ │ └── test.ini │ ├── output.ok │ └── test.c ├── test05 │ ├── Makefile │ ├── README │ ├── data │ │ └── test.ini │ ├── output.ok │ └── test.c ├── test06 │ ├── Makefile │ ├── README │ ├── data │ │ └── test.ini │ ├── output.ok │ └── test.c ├── test07 │ ├── Makefile │ ├── README │ ├── output.ok │ └── test.c ├── test08 │ ├── Makefile │ ├── README │ ├── data │ │ └── test.ini │ ├── output.ok │ └── test.c ├── test09 │ ├── Makefile │ ├── README │ ├── data │ │ └── test.ini │ ├── output.ok │ └── test.c ├── test10 │ ├── Makefile │ ├── README │ ├── data │ │ └── test.ini │ ├── output.ok │ └── test.c ├── test11 │ ├── Makefile │ ├── README │ ├── output.ok │ └── test.c └── test12 │ ├── Makefile │ ├── README │ ├── output.ok │ └── test.c └── update_git_submodules /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.swp 3 | Session.vim 4 | /src/sms 5 | /src/phone_number_validator 6 | /src/pnv_json 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libamievent"] 2 | path = libamievent 3 | url = https://github.com/andrewjsi/libamievent 4 | -------------------------------------------------------------------------------- /INCOMPATIBLE_CHANGES.txt: -------------------------------------------------------------------------------- 1 | # You can git diff this file 2 | 3 | - SMSAMI_CONFIG environment variable renamed to AMISMS_CONFIG 4 | 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: 3 | ifeq ($(wildcard libamievent/ami.h),) 4 | @echo "* " 5 | @echo "* libamievent submodule not found!" 6 | @echo "* To download this project with all submodules included, run:" 7 | @echo "* " 8 | @echo "* git clone --recursive git://github.com/andrewjsi/amisms" 9 | @echo "* " 10 | @exit 1 11 | endif 12 | 13 | cd src; $(MAKE) $(MFLAGS) all 14 | 15 | .PHONY: test 16 | test: all 17 | cd t; $(MAKE) $(MFLAGS) all 18 | 19 | .PHONY: clean 20 | clean: 21 | cd src; $(MAKE) $(MFLAGS) clean 22 | cd t; $(MAKE) $(MFLAGS) clean 23 | 24 | .PHONY: install 25 | install: all 26 | install src/sms /usr/local/bin 27 | install src/phone_number_validator /usr/local/bin/phone_number_validator 28 | 29 | .PHONY: uninstall 30 | uninstall: all 31 | rm -f /usr/local/bin/sms 32 | rm -f /usr/local/bin/phone_number_validator 33 | 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SMS Sender via Asterisk chan_dongle 2 | 3 | AMISMS is a simple command-line tool written in C, which connect to a local or 4 | remote Asterisk PBX via Asterisk Manager Interface and send SMS over a USB 5 | Huawei GSM/UMTS modem via chan_dongle channel driver. 6 | 7 | AMISMS use the [asynchronous event-driven libamievent 8 | library](https://github.com/andrewjsi/libamievent), which is present as 9 | submodule in this repository. 10 | 11 | ## Requirements 12 | 13 | * gcc, make, binutils 14 | * libev 15 | 16 | For Debian users: 17 | 18 | apt-get install build-essential libev-dev 19 | 20 | For Gentoo users: 21 | 22 | emerge -av libev 23 | 24 | * and access to a working system with Asterisk + chan_dongle + Huawei/ZTE 25 | dongle + SIM card. 26 | 27 | ## Install 28 | 29 | Clone the amisms repo with all submodules. Optionally run the 30 | `./update_git_submodules script` to sync submodules. 31 | 32 | git clone --recursive git://github.com/andrewjsi/amisms 33 | 34 | Compile the source, test and install: 35 | 36 | make 37 | make test 38 | sudo make install 39 | 40 | ## Configuration 41 | 42 | The AMISMS search the configuration file at __/home/YOURUSER/.smsrc__ default. 43 | This is an ini-style config file, which contains the AMI server credentials and 44 | the name of the dongle device. Example config file is located in the doc 45 | directory. The simplest configuration looks like this: 46 | 47 | ``` 48 | [device0] 49 | host = localhost ; hostname or IP address where Asterisk is running 50 | port = 5038 ; port where AMI is listening, see asterisk/manager.conf 51 | username = amiuser ; name of Asterisk Manager user which have flag 'call' 52 | password = amipassword ; plaintext password for AMI user 53 | dongle = dongle0 ; dongle name in asterisk/chan_dongle.conf 54 | ``` 55 | 56 | ## Usage 57 | ``` 58 | usage: sms 59 | or sms [OPTIONS] [message text] 60 | 61 | Options 62 | -v, --verbose verbose output to stderr 63 | -f, --flash flash SMS 64 | -s, --stdin read message text from stdin 65 | ``` 66 | ## Example 67 | 68 | Send a simple message: 69 | 70 | sms +36201234567 "Hello, this is AMISMS" 71 | 72 | Send kernel version to mobile phone's display: 73 | 74 | uname -a | sms --flash --stdin +36701234567 75 | 76 | ## Known bugs 77 | 78 | ## Caveats 79 | 80 | * the PDU routines not perfect, currently only 7bit GSM charset allowed 81 | * AMISMS can send up to 160 characters long messages, multi-part SMS not yet allowed 82 | * only tested on Linux, but we have use this software in production environment 83 | 84 | ## Todo, Future 85 | 86 | * UTF-8 / multibyte message text, ISO, binary, alphabets in PDU 87 | * multi-part SMS 88 | * smooth exit codes 89 | * pretty error and status messages to the user 90 | * easy to parse error and status messages to the calling program 91 | * chan_dongle query functions (dongle state, rssi, etc..) 92 | * manage multiple dongles and AMI servers 93 | * better console management, stdout, stderr, verbose and debug messages 94 | * well-written documentation 95 | * the ability to be integrated with other systems as easy as possible 96 | 97 | ## Source code 98 | 99 | The source code is well written as far as possible. We do not use tabs, instead 100 | of 4 spaces is indented. All identifiers, including variables, function names 101 | and macros written in English, but some comments and commits in Hungarian is, 102 | because we are speaking and thinking in Hungarian. Nevertheless, we try to 103 | write everything in English in the future. 104 | 105 | ## Contribution 106 | 107 | It is an open source project, which is to be better and better. If you have any 108 | ideas or find an error, or get stuck, you can contact us, please file a bug 109 | report or send a pull-request! 110 | 111 | ## License 112 | 113 | [_GPL2_](https://www.gnu.org/licenses/gpl-2.0.html) 114 | 115 | (C) Copyright 2010-2014 Andras Jeszenszky, [JSS & Hayer 116 | IT](http://www.jsshayer.hu) All Rights Reserved. 117 | -------------------------------------------------------------------------------- /doc/NOTES.txt: -------------------------------------------------------------------------------- 1 | Ígéretes PDU Kekeskavi alapján: 2 | https://github.com/vilts/PDU 3 | 4 | Megvizsgálni ezt a patch-et: 5 | http://code.google.com/p/asterisk-chan-dongle/issues/detail?id=1 6 | 7 | TODO: 8 | - stdin mód ütközik az absolute timeout-al 9 | - stdin módban csak a szöveg begépelése után derül ki, hogy rossz a telefonszám 10 | - pnv-t összehozni ezzel: https://github.com/googlei18n/libphonenumber 11 | -------------------------------------------------------------------------------- /doc/amisms_example.conf: -------------------------------------------------------------------------------- 1 | ; AMISMS example configuration 2 | 3 | ; force phone number checking 4 | pnv = on 5 | 6 | ; default region for local format phone number checking 7 | default-locale = hu 8 | 9 | ; default device to use 10 | default = device0 11 | 12 | [device0] 13 | host = localhost ; hostname or IP address where Asterisk is running 14 | port = 5038 ; port where AMI is listening, see asterisk/manager.conf 15 | username = amiuser ; name of Asterisk Manager user which have flag 'call' 16 | password = amipassword ; plaintext password for AMI user 17 | dongle = dongle0 ; dongle name in asterisk/chan_dongle.conf 18 | 19 | [another-mobile-provider] 20 | host = 21 | port = 5038 22 | username = asterisk_ami_user 23 | password = asterisk_ami_password 24 | dongle = dongle0 25 | 26 | -------------------------------------------------------------------------------- /doc/chan_dongle_issue1.patch: -------------------------------------------------------------------------------- 1 | Index: at_command.c 2 | =================================================================== 3 | --- at_command.c (revision 24) 4 | +++ at_command.c (working copy) 5 | @@ -133,7 +133,7 @@ 6 | static const char cmd19[] = "AT+CSSN=1,1\r"; 7 | static const char cmd21[] = "AT+CSCS=\"UCS2\"\r"; 8 | 9 | - static const char cmd22[] = "AT+CPMS=\"ME\",\"ME\",\"ME\"\r"; 10 | + static const char cmd22[] = "AT+CPMS=\"SM\",\"SM\",\"SM\"\r"; 11 | static const char cmd23[] = "AT+CNMI=2,1,0,0,0\r"; 12 | static const char cmd24[] = "AT+CSQ\r"; 13 | 14 | Index: at_response.c 15 | =================================================================== 16 | --- at_response.c (revision 24) 17 | +++ at_response.c (working copy) 18 | @@ -1198,9 +1198,17 @@ 19 | pvt_try_restate(pvt); 20 | 21 | cmgr = err_pos = ast_strdupa (str); 22 | - err = at_parse_cmgr (&err_pos, len, oa, sizeof(oa), &oa_enc, &msg, &msg_enc); 23 | - if (err) 24 | + err = at_parse_cmgr (&err_pos, len, oa, sizeof(oa), &oa_enc, &msg, &msg_enc); // YYY 25 | + if (err == (void*)0x1) /* HACK! */ 26 | { 27 | + char buf[64]; 28 | + int res = (int)msg; 29 | + snprintf(buf, 64, "Delivered\r\nForeignID: %d", res); 30 | + manager_event_sent_notify(PVT_ID(pvt), "SMS", 0x0 /* task is popped */, buf); 31 | + return 0; 32 | + } 33 | + else if (err) 34 | + { 35 | ast_log (LOG_WARNING, "[%s] Error parsing incoming message '%s' at possition %d: %s\n", PVT_ID(pvt), str, (int)(err_pos - cmgr), err); 36 | return 0; 37 | } 38 | @@ -1721,11 +1729,23 @@ 39 | case RES_CSSU: 40 | case RES_SRVST: 41 | case RES_CVOICE: 42 | - case RES_CMGS: 43 | case RES_CPMS: 44 | case RES_CONF: 45 | return 0; 46 | 47 | + case RES_CMGS: 48 | + /* Abuse the fact that we know how the manager 49 | + * message are formatted: CRLF separated headers 50 | + * with colon between key and value */ 51 | + { 52 | + char buf[64]; 53 | + int res = at_parse_cmgs(str); 54 | + const at_queue_task_t * task = at_queue_head_task (pvt); 55 | + snprintf(buf, 64, "Sending\r\nForeignID: %d", res); 56 | + manager_event_sent_notify(PVT_ID(pvt), "SMS", task, buf); 57 | + } 58 | + return 0; 59 | + 60 | case RES_OK: 61 | at_response_ok (pvt, at_res); 62 | return 0; 63 | Index: pdu.c 64 | =================================================================== 65 | --- pdu.c (revision 24) 66 | +++ pdu.c (working copy) 67 | @@ -797,10 +797,35 @@ 68 | err = "Can't parse length of OA"; 69 | } 70 | } 71 | + else if(PDUTYPE_MTI(pdu_type) == PDUTYPE_MTI_SMS_STATUS_REPORT) 72 | + { 73 | + int reference = pdu_parse_byte(pdu, &pdu_length); 74 | + /* Skip over 8 bytes TP-DA */ 75 | + if (reference >= 0 && pdu_length >= 8) { 76 | + (*pdu) += 8; 77 | + pdu_length -= 8; 78 | + /* Skip over 7 bytes timestamp TP-SCTS */ 79 | + if (pdu_parse_timestamp(pdu, &pdu_length) >= 0 && 80 | + /* Skip over 7 bytes timestamp TP-DT */ 81 | + pdu_parse_timestamp(pdu, &pdu_length) >= 0) { 82 | + int tp_status = pdu_parse_byte(pdu, &pdu_length); 83 | + if ((tp_status & 0xf) == 0) { 84 | + err = (void*)0x1; /* HACK! */ 85 | + *msg = (void*)reference; 86 | + } else { 87 | + err = "Good report, but delivery failed"; 88 | + } 89 | + } else { 90 | + err = "FIXME error 1"; 91 | + } 92 | + } else { 93 | + err = "FIXME error 2"; 94 | + } 95 | + } 96 | else 97 | { 98 | *pdu -= 2; 99 | - err = "Unhandled PDU Type MTI only SMS-DELIVER supported"; 100 | + err = "Unhandled PDU Type MTI only SMS-DELIVER/SMS-STATUS-REPORT supported"; 101 | } 102 | } 103 | else 104 | Index: at_parse.c 105 | =================================================================== 106 | --- at_parse.c (revision 24) 107 | +++ at_parse.c (working copy) 108 | @@ -385,6 +385,27 @@ 109 | return rv; 110 | } 111 | 112 | +/*! 113 | + * \brief Parse a +CMGS notification 114 | + * \param str -- string to parse (null terminated) 115 | + * \return -1 on error (parse error) or the first integer value found 116 | + * \todo FIXME: parse [,] value correctly 117 | + */ 118 | + 119 | +EXPORT_DEF int at_parse_cmgs (const char* str) 120 | +{ 121 | + int cmgs = -1; 122 | + 123 | + /* 124 | + * parse CMGS info in the following format: 125 | + * +CMGS:[,] 126 | + * (sscanf is lax about extra spaces) 127 | + * TODO: not ignore parse errors ;) 128 | + */ 129 | + sscanf (str, "+CMGS:%d", &cmgs); 130 | + return cmgs; 131 | +} 132 | + 133 | /*! 134 | * \brief Parse a CUSD answer 135 | * \param str -- string to parse (null terminated) 136 | Index: at_parse.h 137 | =================================================================== 138 | --- at_parse.h (revision 24) 139 | +++ at_parse.h (working copy) 140 | @@ -15,6 +15,7 @@ 141 | EXPORT_DECL int at_parse_creg (char* str, unsigned len, int* gsm_reg, int* gsm_reg_status, char** lac, char** ci); 142 | EXPORT_DECL int at_parse_cmti (const char* str); 143 | EXPORT_DECL const char* at_parse_cmgr (char** str, size_t len, char* oa, size_t oa_len, str_encoding_t* oa_enc, char** msg, str_encoding_t* msg_enc); 144 | +EXPORT_DECL int at_parse_cmgs (const char* str); 145 | EXPORT_DECL int at_parse_cusd (char* str, int * type, char ** cusd, int * dcs); 146 | EXPORT_DECL int at_parse_cpin (char* str, size_t len); 147 | EXPORT_DECL int at_parse_csq (const char* str, int* rssi); 148 | -------------------------------------------------------------------------------- /pgsql/.gitignore: -------------------------------------------------------------------------------- 1 | /smsd-pgsql 2 | -------------------------------------------------------------------------------- /pgsql/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -O2 -Wall -Wno-strict-aliasing 3 | CFLAGS += -ggdb 4 | CFLAGS += -I../libamievent -I../libamievent/libc-jss 5 | CFLAGS += -I/usr/include/postgresql 6 | LDFLAGS = -lev -lm -lpq 7 | 8 | OBJ += ../libamievent/libc-jss/ini/ini.o 9 | OBJ += ../libamievent/libc-jss/logger.o 10 | OBJ += ../libamievent/libc-jss/misc.o 11 | OBJ += pgsql.o 12 | PROGS=smsd-pgsql 13 | 14 | .PHONY: all 15 | all: $(patsubst %, %.o, $(PROGS)) $(OBJ) $(PROGS) 16 | 17 | %: %.o $(OBJ) 18 | gcc $(CFLAGS) -o $@ $< $(OBJ) $(LDFLAGS) 19 | 20 | clean: 21 | rm -f *.o $(OBJ) $(PROGS) 22 | 23 | -------------------------------------------------------------------------------- /pgsql/pgsql.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "misc.h" 10 | #include "pgsql.h" 11 | 12 | #include "debug.h" 13 | 14 | static void error_set (pg_t *pg, const char *fmt, ...) { 15 | va_list ap; 16 | va_start(ap, fmt); 17 | vsnprintf(pg->error_message, sizeof(pg->error_message), fmt, ap); 18 | va_end(ap); 19 | 20 | fprintf(stderr, "[error_set] %s\n", chomp(pg->error_message)); 21 | } 22 | 23 | static void error_clear (pg_t *pg) { 24 | pg->error_message[0] = '\0'; 25 | } 26 | 27 | static int connect_to_postgresql (pg_t *pg) { 28 | pg->conn = PQconnectdb(pg->connect_string); 29 | 30 | if (!pg->conn) { 31 | error_set(pg, "libpq error: PQconnectdb returned NULL."); 32 | return -1; 33 | } 34 | if (PQstatus(pg->conn) != CONNECTION_OK) { 35 | error_set(pg, PQerrorMessage(pg->conn)); 36 | return -1; 37 | } 38 | 39 | pg->connected = 1; 40 | return 0; 41 | } 42 | 43 | pg_t *pg_new (const char *connect_string) { 44 | pg_t *pg = malloc(sizeof(*pg)); 45 | if (pg == NULL) { 46 | fprintf(stderr, "pg_new() returned NULL"); 47 | return NULL; 48 | } 49 | bzero(pg, sizeof(*pg)); // minden NULL 50 | 51 | scpy(pg->connect_string, connect_string); 52 | connect_to_postgresql(pg); 53 | return pg; 54 | } 55 | 56 | int pg_query (pg_t *pg, const char *fmt, ...) { 57 | error_clear(pg); 58 | 59 | va_list ap; 60 | va_start(ap, fmt); 61 | vsnprintf(pg->buf_query, sizeof(pg->buf_query), fmt, ap); 62 | va_end(ap); 63 | 64 | pg->res = PQexec(pg->conn, pg->buf_query); 65 | 66 | if (PQresultStatus(pg->res) == PGRES_BAD_RESPONSE || 67 | PQresultStatus(pg->res) == PGRES_NONFATAL_ERROR || 68 | PQresultStatus(pg->res) == PGRES_FATAL_ERROR 69 | ) { 70 | error_set(pg, PQerrorMessage(pg->conn)); 71 | PQclear(pg->res); 72 | pg->res = NULL; 73 | return -1; 74 | } 75 | 76 | return 0; 77 | } 78 | 79 | // ITT TARTOK: az adatokat vissza kell adni a hívónak. Ehhez a ds4 / db.c 80 | // megvalósítást kell alkalmazni! 81 | void pg_get_res (pg_t *pg) { 82 | int nFields, i, j; 83 | 84 | /* first, print out the attribute names */ 85 | nFields = PQnfields(pg->res); 86 | for (i = 0; i < nFields; i++) 87 | printf("%-15s", PQfname(pg->res, i)); 88 | printf("\n\n"); 89 | 90 | /* next, print out the rows */ 91 | for (i = 0; i < PQntuples(pg->res); i++) 92 | { 93 | for (j = 0; j < nFields; j++) 94 | printf("%-15s", PQgetvalue(pg->res, i, j)); 95 | printf("\n"); 96 | } 97 | } 98 | 99 | const char *pg_error (pg_t *pg) { 100 | if (!strlen(pg->error_message)) { 101 | return NULL; 102 | } else { 103 | return pg->error_message; 104 | } 105 | } 106 | 107 | -------------------------------------------------------------------------------- /pgsql/pgsql.h: -------------------------------------------------------------------------------- 1 | #ifndef PG_H_INCLUDED 2 | #define PG_H_INCLUDED 3 | 4 | #include 5 | 6 | typedef struct pg_t { 7 | char connect_string[256]; // PostgreSQL connect string 8 | char buf_query[4096]; // lekérdezés buffere 9 | int connected:1; // PostgreSQL kapcsolat aktív? 10 | PGconn *conn; // PGconn 11 | PGresult *res; // PGresult 12 | char error_message[512]; // hibaüzenet ide kerül 13 | } pg_t; 14 | 15 | pg_t *pg_new (const char *connect_string); 16 | 17 | int pg_query (pg_t *pg, const char *fmt, ...); 18 | 19 | const char *pg_error (pg_t *pg); 20 | 21 | void pg_get_res (pg_t *pg); 22 | 23 | #endif // #ifndef PG_H_INCLUDED 24 | -------------------------------------------------------------------------------- /pgsql/smsd-pgsql.c: -------------------------------------------------------------------------------- 1 | // TODO: SIGALRM stílusú absolute timeout 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "pgsql.h" 13 | 14 | pg_t *pg1; 15 | 16 | void create_database () { 17 | if (pg_query(pg1, "\ 18 | DROP TABLE IF EXISTS sms; \ 19 | CREATE TABLE IF NOT EXISTS sms ( \ 20 | id SERIAL PRIMARY KEY, \ 21 | phone_number CHARACTER VARYING(32) NOT NULL, \ 22 | text CHARACTER VARYING(1080) NOT NULL, \ 23 | sent BOOLEAN DEFAULT FALSE, \ 24 | delivered BOOLEAN DEFAULT FALSE, \ 25 | error BOOLEAN DEFAULT FALSE, \ 26 | error_message CHARACTER VARYING(128) DEFAULT NULL \ 27 | );")) { 28 | fprintf(stderr, "pg_query(): %s\n", pg_error(pg1)); 29 | } 30 | 31 | pg_query(pg1, "INSERT INTO sms (phone_number, text) VALUES ('06-20-772-0300', 'proba teszt');"); 32 | pg_query(pg1, "INSERT INTO sms (phone_number, text) VALUES ('06207720300', 'teszt2');"); 33 | } 34 | 35 | int main (int argc, char *argv[]) { 36 | pg1 = pg_new("hostaddr = '127.0.0.1' port = '5432' dbname = 'sms' user = 'sms' password = 'SokSMS'"); 37 | 38 | if (pg_error(pg1)) { 39 | fprintf(stderr, "Hiba pg_new() után: %s\n", pg_error(pg1)); 40 | } 41 | 42 | create_database(); 43 | 44 | pg_query(pg1, "SELECT * FROM sms WHERE sent=false;"); 45 | 46 | pg_get_res(pg1); 47 | 48 | return 0; 49 | } 50 | 51 | 52 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS += -O2 -Wall -Wno-strict-aliasing 3 | # CFLAGS += -ggdb 4 | CFLAGS += -I../libamievent -I../libamievent/libc-jss -I../pdu 5 | LDFLAGS = -lev -lm 6 | 7 | OBJ += ../libamievent/libc-jss/ini/ini.o 8 | OBJ += ../libamievent/libc-jss/netsocket.o 9 | OBJ += ../libamievent/libc-jss/logger.o 10 | OBJ += ../libamievent/libc-jss/misc.o 11 | OBJ += ../libamievent/ami.o 12 | OBJ += pdu/charset.o 13 | OBJ += pdu/compat.o 14 | OBJ += pdu/pdu.o 15 | OBJ += option.o 16 | OBJ += conf.o 17 | OBJ += pnv.o 18 | OBJ += verbose.o 19 | PROGS=sms phone_number_validator pnv_json 20 | 21 | # take version from GIT 22 | GIT_VERSION = $(shell sh -c 'git describe --abbrev=6 --dirty --always --tags') 23 | CFLAGS += -DVERSION=\"$(GIT_VERSION)\" 24 | 25 | .PHONY: all 26 | all: $(patsubst %, %.o, $(PROGS)) $(OBJ) $(PROGS) 27 | 28 | %: %.o $(OBJ) 29 | gcc $(CFLAGS) -o $@ $< $(OBJ) $(LDFLAGS) 30 | 31 | clean: 32 | rm -f *.o $(OBJ) $(PROGS) 33 | 34 | -------------------------------------------------------------------------------- /src/NOTES.txt: -------------------------------------------------------------------------------- 1 | AMISMS 2 | ====== 3 | 4 | Send SMS over Asterisk Manager Interface with chan_dongle 5 | 6 | sms küldő függvényt csinálni, egy API-t, amin keresztül működik. Lehetne 7 | forgatni külön amisms binárist meg libamisms libet 8 | 9 | amisms_t *amisms = NULL; 10 | amisms = amisms_new(); 11 | amisms_parse_configfile(amisms, "~/.smsrc"); 12 | amisms_option("host", "localhost"); 13 | amisms_option("port", "54"); 14 | amisms_options("port", "54", "host", "localhost", NULL); 15 | amisms_set_callback_print(*void); 16 | amisms_send_sms 17 | amisms_strerror(amisms); 18 | -------------------------------------------------------------------------------- /src/conf.c: -------------------------------------------------------------------------------- 1 | /* conf.c - config file handling 2 | * Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu 3 | * 4 | * This program is free software, distributed under the terms of 5 | * the GNU General Public License Version 2. See the LICENSE file 6 | * at the top of the source tree. 7 | */ 8 | 9 | #include 10 | #include 11 | 12 | #include "debug.h" 13 | #include "misc.h" 14 | #include "ini/ini.h" 15 | #include "uthash.h" 16 | #include "conf.h" 17 | 18 | 19 | static char config_file[128]; 20 | static struct conf_root_t *conf_root_struct; 21 | 22 | // device hash head 23 | static struct conf_device_t *conf_device_hash = NULL; 24 | 25 | // dummy hash item for mismatched search in conf_device() 26 | static const struct conf_device_t conf_device_hash_dummy; 27 | 28 | static int parse_error = 0; 29 | 30 | 31 | void conf_dump () { 32 | printf("default = %s\n", conf_root_struct->default_device_name); 33 | printf("pnv = %d\n", conf_root_struct->pnv); 34 | printf("default-locale = %s\n", conf_root_struct->default_locale); 35 | 36 | printf("\n"); 37 | struct conf_device_t *s, *tmp; 38 | HASH_ITER(hh, conf_device_hash, s, tmp) { 39 | printf("%s\n", s->device_name); 40 | printf("\thost = %s\n", s->host); 41 | printf("\tport = %s\n", s->port); 42 | printf("\tusername = %s\n", s->username); 43 | printf("\tpassword = %s\n", s->password); 44 | printf("\tdongle = %s\n", s->dongle); 45 | } 46 | } 47 | 48 | // this function called on every line while parsing config file 49 | static int ini_handler (void *userdata, const char *section, const char *name, const char *value) { 50 | #define MATCH(s) (strcmp(name, s) == 0) 51 | 52 | // without any sections 53 | if (!strcmp(section, "")) { 54 | #undef CPY 55 | #define CPY(h) strncpy(conf_root_struct->h, value, sizeof(conf_root_struct->h) - 1) 56 | if (MATCH("default")) { 57 | CPY(default_device_name); 58 | 59 | } else if (MATCH("default-locale")) { 60 | CPY(default_locale); 61 | 62 | } else if (MATCH("pnv")) { 63 | if (!strcmp(value, "on")) { 64 | conf_root_struct->pnv = CONF_PNV_ON; 65 | } else if (!strcmp(value, "off")) { 66 | conf_root_struct->pnv = CONF_PNV_OFF; 67 | } else if (!strcmp(value, "force")) { 68 | conf_root_struct->pnv = CONF_PNV_FORCE; 69 | } else { 70 | printf("config option \"pnv\" must be \"on\", \"off\" or \"forced\""); 71 | parse_error = 1; 72 | return 0; 73 | } 74 | 75 | } else { 76 | printf("unknown config option \"%s\" in section \"%s\"\n", name, section); 77 | parse_error = 1; 78 | return 0; // unknown section/name error ??? 79 | 80 | } 81 | 82 | // with named sections 83 | } else { 84 | #undef CPY 85 | #define CPY(h) strncpy(item->h, value, sizeof(item->h) - 1) 86 | struct conf_device_t *item; 87 | HASH_FIND_STR(conf_device_hash, section, item); 88 | 89 | // create new hash item if not exists 90 | if (item == NULL) { 91 | item = (struct conf_device_t*)malloc(sizeof(*item)); 92 | memset(item, 0, sizeof(*item)); 93 | scpy(item->device_name, section); 94 | HASH_ADD_STR(conf_device_hash, device_name, item); 95 | 96 | } 97 | 98 | if (MATCH("host")) { 99 | CPY(host); 100 | 101 | } else if (MATCH("port")) { 102 | CPY(port); 103 | 104 | } else if (MATCH("username")) { 105 | CPY(username); 106 | 107 | } else if (MATCH("password")) { 108 | CPY(password); 109 | 110 | } else if (MATCH("dongle")) { 111 | CPY(dongle); 112 | 113 | } else { 114 | printf("unknown config option \"%s\" in section \"%s\"\n", name, section); 115 | parse_error = 1; 116 | return 0; // unknown section/name error ??? 117 | 118 | } 119 | 120 | } 121 | 122 | return 1; 123 | } 124 | 125 | void config_check () { 126 | struct conf_device_t *s, *tmp; 127 | int at_least_one_device = 0; 128 | 129 | HASH_ITER(hh, conf_device_hash, s, tmp) { 130 | at_least_one_device = 1; 131 | if (!strlen(s->host)) { 132 | strcpy(s->host, "localhost"); 133 | } 134 | 135 | if (!strlen(s->port)) 136 | strcpy(s->port, "5038"); 137 | 138 | if (atoi(s->port) < 1 || atoi(s->port) > 65534) { 139 | printf("unknown port \"%s\" in device \"%s\"\n", s->port, s->device_name); 140 | parse_error = 1; 141 | } 142 | 143 | if (!strlen(s->username)) { 144 | printf("no username defined in device \"%s\"\n", s->device_name); 145 | parse_error = 1; 146 | } 147 | 148 | if (!strlen(s->password)) { 149 | printf("no password defined in device \"%s\"\n", s->device_name); 150 | parse_error = 1; 151 | } 152 | 153 | if (!strlen(s->dongle)) { 154 | printf("no dongle defined in device \"%s\"\n", s->device_name); 155 | parse_error = 1; 156 | } 157 | } 158 | 159 | if (!at_least_one_device) { 160 | printf("At least one device must be specified in configuration!\n"); 161 | parse_error = 1; 162 | } 163 | 164 | // if no default device specified, then the first device to be default 165 | if (!strlen(conf_root_struct->default_device_name)) { 166 | if (conf_device_hash != NULL) { 167 | scpy(conf_root_struct->default_device_name, conf_device_hash->device_name); 168 | } 169 | } 170 | 171 | HASH_FIND_STR(conf_device_hash, conf_root_struct->default_device_name, s); 172 | if (s == NULL) { 173 | if (strlen(conf_root_struct->default_device_name)) { 174 | printf("Default device not found: \"%s\"\n", conf_root_struct->default_device_name); 175 | } 176 | parse_error = 1; 177 | } 178 | } 179 | 180 | void conf_set_config_file (const char *file_expression) { 181 | if (file_expression == NULL || strlen(file_expression) == 0) 182 | return; 183 | 184 | // parse config file name as shell expression 185 | wordexp_t p; 186 | if (wordexp(file_expression, &p, WRDE_NOCMD)) { 187 | printf("Config file name mismatch: \"%s\"\n", file_expression); 188 | parse_error = 1; 189 | config_file[0] = '\0'; // empty string 190 | return; 191 | } 192 | 193 | scpy(config_file, p.we_wordv[0]); 194 | wordfree(&p); 195 | } 196 | 197 | void conf_unload () { 198 | if (conf_root_struct != NULL) { 199 | free(conf_root_struct); 200 | conf_root_struct = NULL; 201 | } 202 | 203 | if (conf_device_hash != NULL) { 204 | struct conf_device_t *s, *tmp; 205 | HASH_ITER(hh, conf_device_hash, s, tmp) { 206 | HASH_DEL(conf_device_hash, s); 207 | free(s); 208 | } 209 | conf_device_hash = NULL; 210 | } 211 | } 212 | 213 | int conf_load () { 214 | parse_error = 0; 215 | conf_root_struct = malloc(sizeof(*conf_root_struct)); 216 | memset(conf_root_struct, 0, sizeof(*conf_root_struct)); 217 | 218 | // memset(&conf_device_hash_dummy, 0, sizeof(conf_device_hash_dummy)); 219 | 220 | // config betöltése 221 | if (ini_parse(config_file, ini_handler, NULL) < 0) { 222 | printf("Can't load configuration from %s: %s\n", config_file, strerror(errno)); 223 | goto err; 224 | } 225 | 226 | config_check(); 227 | 228 | if (parse_error) { 229 | printf("Parse error in config file: %s\n", config_file); 230 | goto err; 231 | } 232 | 233 | return 0; 234 | 235 | err: 236 | conf_unload(); 237 | return -1; 238 | } 239 | 240 | const struct conf_root_t *conf_root () { 241 | return conf_root_struct; 242 | } 243 | 244 | const struct conf_device_t *conf_device (const char *device_name) { 245 | struct conf_device_t *item; 246 | HASH_FIND_STR(conf_device_hash, device_name, item); 247 | 248 | if (item == NULL) 249 | return &conf_device_hash_dummy; 250 | else 251 | return item; // TODO: segfault veszély, ha nem létező device_name esetén a hívó nem kezeli a NULL eseményt 252 | } 253 | 254 | int conf_device_exists (const char *device_name) { 255 | struct conf_device_t *item; 256 | HASH_FIND_STR(conf_device_hash, device_name, item); 257 | 258 | return (item == NULL) ? 0 : 1; 259 | } 260 | 261 | -------------------------------------------------------------------------------- /src/conf.h: -------------------------------------------------------------------------------- 1 | #include "uthash.h" 2 | 3 | struct conf_device_t { 4 | char device_name[64]; // config file [section] name 5 | char host[64]; 6 | char port[8]; 7 | char username[64]; 8 | char password[64]; 9 | char dongle[64]; 10 | UT_hash_handle hh; // makes this structure hashable with Troy D. Hanson's uthash.h 11 | }; 12 | 13 | struct conf_root_t { 14 | char default_device_name[64]; // default device to use 15 | char default_locale[8]; // default two-letter locale, used by pnv 16 | enum { 17 | CONF_PNV_ON, 18 | CONF_PNV_OFF, 19 | CONF_PNV_FORCE, 20 | } pnv; 21 | }; 22 | 23 | void conf_unload (); 24 | void conf_set_config_file (const char *file_expression); 25 | int conf_load (); 26 | void conf_dump (); 27 | const struct conf_root_t *conf_root (); 28 | const struct conf_device_t *conf_device (const char *device_name); 29 | int conf_device_exists (const char *device_name); 30 | -------------------------------------------------------------------------------- /src/defaults.h: -------------------------------------------------------------------------------- 1 | /* defaults.h 2 | * Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu 3 | * 4 | * This program is free software, distributed under the terms of 5 | * the GNU General Public License Version 2. See the LICENSE file 6 | * at the top of the source tree. 7 | */ 8 | 9 | #define DEFAULT_CONFIG_FILE "~/.smsrc" 10 | 11 | -------------------------------------------------------------------------------- /src/option.c: -------------------------------------------------------------------------------- 1 | /* option.c - parse command line arguments 2 | * Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu 3 | * 4 | * This program is free software, distributed under the terms of 5 | * the GNU General Public License Version 2. See the LICENSE file 6 | * at the top of the source tree. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include // basename() 15 | #include 16 | #include // isprint() 17 | 18 | #include "defaults.h" 19 | #include "option.h" 20 | #include "misc.h" 21 | #include "debug.h" 22 | #include "verbose.h" 23 | 24 | #define DUMPI(e) fprintf(stderr, "%13s = %d\n", #e, option.e) 25 | #define DUMPS(e) fprintf(stderr, "%13s = \"%s\"\n", #e, option.e) 26 | void option_dump () { 27 | // DUMPI(debug); 28 | DUMPS(config); 29 | DUMPI(flash); 30 | DUMPI(help); 31 | DUMPI(stdin); 32 | DUMPS(message_text); 33 | DUMPS(phone_number); 34 | } 35 | 36 | static struct option longopts[] = { // please :sort this table after modify 37 | // {"debug", no_argument, &option.debug, 1}, 38 | {"config", required_argument, 0, 'c'}, 39 | {"flash", no_argument, 0, 'f'}, 40 | {"help", no_argument, 0, 'h'}, 41 | {"nopnv", no_argument, &option.nopnv, 1}, 42 | {"stdin", no_argument, 0, 's'}, 43 | {"verbose", no_argument, 0, 'v'}, 44 | {0, 0, 0, 0} 45 | }; 46 | 47 | static const char *optstring = "c:dfhsv"; 48 | static char my_basename[64]; 49 | static char error_message[512]; 50 | 51 | static void print_logo () { 52 | printf( 53 | "JSS SMS Sender version %s\n" 54 | "(c) 2012-2014 JSS&Hayer - http://amisms.jss.hu\n" 55 | "\n" 56 | , VERSION 57 | ); 58 | } 59 | 60 | void option_print_usage (char *fmt, ...) { 61 | print_logo(); 62 | printf("for help: sms -h\n"); 63 | printf("usage: sms [message text]\n"); 64 | 65 | if (fmt != NULL) { 66 | chomp(fmt); 67 | va_list ap; 68 | va_start(ap, fmt); 69 | fprintf(stderr, "\n"); 70 | vfprintf(stderr, fmt, ap); 71 | fprintf(stderr, "\n"); 72 | va_end(ap); 73 | } 74 | exit(-1); 75 | } 76 | 77 | void option_print_help (char *fmt, ...) { 78 | print_logo(); 79 | 80 | if (fmt != NULL) { 81 | va_list ap; 82 | va_start(ap, fmt); 83 | vfprintf(stderr, fmt, ap); 84 | fprintf(stderr, "\n\n"); 85 | va_end(ap); 86 | } else { 87 | printf("usage: sms \n"); 88 | printf(" or sms [OPTIONS] [message text]\n"); 89 | printf("\n"); 90 | printf("Options\n"); 91 | printf(" -v, -vv, -vvv increase verbosity level\n"); 92 | printf(" -f, --flash flash SMS\n"); 93 | printf(" -s, --stdin read message text from stdin\n"); 94 | printf(" -c, --config config file (default: %s)\n", DEFAULT_CONFIG_FILE); 95 | printf(" --nopnv skip phone number validation\n"); 96 | } 97 | exit(-1); 98 | } 99 | 100 | int option_parse_args (int argc, char *argv[]) { 101 | scpy(my_basename, basename((char*)argv[0])); 102 | 103 | int getopt_parse_error = 0; 104 | opterr = 0; // tell getopt to avoid print internal error messages 105 | 106 | for (;;) { 107 | /* getopt_long stores the option index here. */ 108 | int option_index = 0; 109 | 110 | int c = getopt_long(argc, argv, optstring, longopts, &option_index); 111 | 112 | /* Detect the end of the options. */ 113 | if (c == -1) 114 | break; 115 | 116 | switch (c) { 117 | case 0: 118 | /* If this option set a flag, do nothing else now. */ 119 | if (longopts[option_index].flag != 0) 120 | break; 121 | printf ("option %s", longopts[option_index].name); 122 | if (optarg) 123 | printf (" with arg %s", optarg); 124 | printf ("\n"); 125 | break; 126 | 127 | case 'c': 128 | scpy(option.config, optarg); 129 | break; 130 | 131 | case 'f': 132 | option.flash = 1; 133 | break; 134 | 135 | case 'h': 136 | option_print_help(NULL); 137 | break; 138 | 139 | case 's': 140 | option.stdin = 1; 141 | break; 142 | 143 | case 'v': 144 | verbose_inc_level(); 145 | break; 146 | 147 | case '?': 148 | getopt_parse_error = 1; 149 | if (optopt == 'c') 150 | concatf(error_message, "Option -c need config file name!\n"); 151 | else if (isprint(optopt)) 152 | concatf(error_message, "Unknown option: `-%c'\n", optopt); 153 | else if (optopt == '\0') 154 | concatf(error_message, "Unknown option: `%s'\n", argv[optind-1]); 155 | else 156 | concatf(error_message, "Unknown option character `\\x%x'\n", optopt); 157 | break; 158 | 159 | default: 160 | abort(); 161 | } 162 | } 163 | 164 | if (getopt_parse_error) 165 | option_print_usage(error_message); 166 | 167 | // 1st argument 168 | if (argc - optind < 1) { 169 | option_print_usage(NULL); 170 | } 171 | scpy(option.phone_number, argv[optind++]); 172 | 173 | // read message text from 2nd argument if the stdin mode not defined 174 | if (!option.stdin) { 175 | if (argc - optind < 1) { 176 | option_print_usage( 177 | "No message text defined in argument!\n" 178 | "You can also specify -s option to read text from stdin." 179 | ); 180 | } 181 | 182 | // read all other arguments to message_text, delimeted with whitespaces 183 | while (optind < argc) { 184 | concatf(option.message_text, "%s ", argv[optind++]); 185 | } 186 | 187 | // remove last whitespace 188 | if (option.message_text[strlen(option.message_text) - 1] == ' ') { 189 | option.message_text[strlen(option.message_text) - 1] = '\0'; 190 | } 191 | } else { 192 | // read from stdin 193 | verbosef(1, "Text? (press control+d to send)\n"); 194 | option.message_text[0] = '\0'; 195 | read_lines_from_stdin(option.message_text, sizeof(option.message_text)); 196 | } 197 | 198 | return 0; 199 | } 200 | 201 | -------------------------------------------------------------------------------- /src/option.h: -------------------------------------------------------------------------------- 1 | /* option.h - parse command line arguments 2 | * Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu 3 | * 4 | * This program is free software, distributed under the terms of 5 | * the GNU General Public License Version 2. See the LICENSE file 6 | * at the top of the source tree. 7 | */ 8 | 9 | void option_set_basename (const char *bn); 10 | int option_parse_args (int argc, char *argv[]); 11 | void option_dump (); 12 | 13 | // if you modify this struct, please consult the option_dump() function in option.c 14 | struct option_t { 15 | char phone_number[128]; 16 | char message_text[1024]; 17 | char config[128]; 18 | int help; 19 | int flash; 20 | int stdin; 21 | int nopnv; 22 | }; 23 | 24 | // this variable holds the all options 25 | struct option_t option; 26 | 27 | -------------------------------------------------------------------------------- /src/pdu/.gitignore: -------------------------------------------------------------------------------- 1 | /test 2 | -------------------------------------------------------------------------------- /src/pdu/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -Wall -ggdb -I../../libamievent/libc-jss -DUSE_ICONV 3 | 4 | OBJ += pdu.o charset.o compat.o ../verbose.o 5 | PROGS=test 6 | 7 | .PHONY: all 8 | all: $(patsubst %, %.o, $(PROGS)) $(OBJ) $(PROGS) 9 | 10 | %: %.o $(OBJ) 11 | gcc $(CFLAGS) -o $@ $< $(OBJ) $(LDFLAGS) 12 | 13 | clean: 14 | rm -f *.o $(OBJ) $(PROGS) 15 | 16 | -------------------------------------------------------------------------------- /src/pdu/charset.c: -------------------------------------------------------------------------------- 1 | /* 2 | SMS Server Tools 3 3 | Copyright (C) 2006- Keijo Kasvi 4 | http://smstools3.kekekasvi.com/ 5 | 6 | Based on SMS Server Tools 2 from Stefan Frings 7 | http://www.meinemullemaus.de/ 8 | SMS Server Tools version 2 and below are Copyright (C) Stefan Frings. 9 | 10 | This program is free software unless you got it under another license directly 11 | from the author. You can redistribute it and/or modify it under the terms of 12 | the GNU General Public License as published by the Free Software Foundation. 13 | Either version 2 of the License, or (at your option) any later version. 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #ifdef USE_ICONV 23 | #include 24 | #include 25 | #endif 26 | #include "charset.h" 27 | #include "compat.h" 28 | #include "pdu.h" 29 | 30 | // For incoming character 0x24 conversion: 31 | // Change this if other than Euro character is wanted, like '?' or '$'. 32 | #define GSM_CURRENCY_SYMBOL_TO_ISO 0xA4 33 | 34 | // For incoming character 0x09 conversion: 35 | // (some reference: ftp://www.unicode.org/Public/MAPPINGS/ETSI/GSM0338.TXT) 36 | // Uncomment this if you want that C-CEDILLA is represented as small c-cedilla: 37 | //#define INCOMING_SMALL_C_CEDILLA 38 | 39 | // iso = ISO8859-15 (you might change the table to any other 8-bit character set) 40 | // sms = sms character set used by mobile phones 41 | 42 | // iso sms 43 | char charset[] = { '@' , 0x00, // COMMERCIAL AT 44 | 0xA3, 0x01, // POUND SIGN 45 | '$' , 0x02, // DOLLAR SIGN 46 | 0xA5, 0x03, // YEN SIGN 47 | 0xE8, 0x04, // LATIN SMALL LETTER E WITH GRAVE 48 | 0xE9, 0x05, // LATIN SMALL LETTER E WITH ACUTE 49 | 0xF9, 0x06, // LATIN SMALL LETTER U WITH GRAVE 50 | 0xEC, 0x07, // LATIN SMALL LETTER I WITH GRAVE 51 | 0xF2, 0x08, // LATIN SMALL LETTER O WITH GRAVE 52 | 53 | #ifdef INCOMING_SMALL_C_CEDILLA 54 | 0xE7, 0x09, // LATIN SMALL LETTER C WITH CEDILLA 55 | #else 56 | 0xC7, 0x09, // LATIN CAPITAL LETTER C WITH CEDILLA 57 | #endif 58 | 59 | 0x0A, 0x0A, // LF 60 | 0xD8, 0x0B, // LATIN CAPITAL LETTER O WITH STROKE 61 | 0xF8, 0x0C, // LATIN SMALL LETTER O WITH STROKE 62 | 0x0D, 0x0D, // CR 63 | 0xC5, 0x0E, // LATIN CAPITAL LETTER A WITH RING ABOVE 64 | 0xE5, 0x0F, // LATIN SMALL LETTER A WITH RING ABOVE 65 | 66 | // ISO8859-7, Capital greek characters 67 | // 0xC4, 0x10, 68 | // 0x5F, 0x11, 69 | // 0xD6, 0x12, 70 | // 0xC3, 0x13, 71 | // 0xCB, 0x14, 72 | // 0xD9, 0x15, 73 | // 0xD0, 0x16, 74 | // 0xD8, 0x17, 75 | // 0xD3, 0x18, 76 | // 0xC8, 0x19, 77 | // 0xCE, 0x1A, 78 | 79 | // ISO8859-1, ISO8859-15 80 | 0x81, 0x10, // GREEK CAPITAL LETTER DELTA 81 | 0x5F, 0x11, // LOW LINE 82 | 0x82, 0x12, // GREEK CAPITAL LETTER PHI 83 | 0x83, 0x13, // GREEK CAPITAL LETTER GAMMA 84 | 0x84, 0x14, // GREEK CAPITAL LETTER LAMDA 85 | 0x85, 0x15, // GREEK CAPITAL LETTER OMEGA 86 | 0x86, 0x16, // GREEK CAPITAL LETTER PI 87 | 0x87, 0x17, // GREEK CAPITAL LETTER PSI 88 | 0x88, 0x18, // GREEK CAPITAL LETTER SIGMA 89 | 0x89, 0x19, // GREEK CAPITAL LETTER THETA 90 | 0x8A, 0x1A, // GREEK CAPITAL LETTER XI 91 | 92 | 0x1B, 0x1B, // ESC 93 | 0xC6, 0x1C, // LATIN CAPITAL LETTER AE 94 | 0xE6, 0x1D, // LATIN SMALL LETTER AE 95 | 0xDF, 0x1E, // LATIN SMALL LETTER SHARP S 96 | 0xC9, 0x1F, // LATIN CAPITAL LETTER E WITH ACUTE 97 | ' ' , 0x20, // SPACE 98 | '!' , 0x21, // EXCLAMATION MARK 99 | 0x22, 0x22, // QUOTATION MARK 100 | '#' , 0x23, // NUMBER SIGN 101 | 102 | // GSM character 0x24 is a "currency symbol". 103 | // This character is never sent. Incoming character is converted without conversion tables. 104 | 105 | '%' , 0x25, // PERSENT SIGN 106 | '&' , 0x26, // AMPERSAND 107 | 0x27, 0x27, // APOSTROPHE 108 | '(' , 0x28, // LEFT PARENTHESIS 109 | ')' , 0x29, // RIGHT PARENTHESIS 110 | '*' , 0x2A, // ASTERISK 111 | '+' , 0x2B, // PLUS SIGN 112 | ',' , 0x2C, // COMMA 113 | '-' , 0x2D, // HYPHEN-MINUS 114 | '.' , 0x2E, // FULL STOP 115 | '/' , 0x2F, // SOLIDUS 116 | '0' , 0x30, // DIGIT 0...9 117 | '1' , 0x31, 118 | '2' , 0x32, 119 | '3' , 0x33, 120 | '4' , 0x34, 121 | '5' , 0x35, 122 | '6' , 0x36, 123 | '7' , 0x37, 124 | '8' , 0x38, 125 | '9' , 0x39, 126 | ':' , 0x3A, // COLON 127 | ';' , 0x3B, // SEMICOLON 128 | '<' , 0x3C, // LESS-THAN SIGN 129 | '=' , 0x3D, // EQUALS SIGN 130 | '>' , 0x3E, // GREATER-THAN SIGN 131 | '?' , 0x3F, // QUESTION MARK 132 | 0xA1, 0x40, // INVERTED EXCLAMATION MARK 133 | 'A' , 0x41, // LATIN CAPITAL LETTER A...Z 134 | 'B' , 0x42, 135 | 'C' , 0x43, 136 | 'D' , 0x44, 137 | 'E' , 0x45, 138 | 'F' , 0x46, 139 | 'G' , 0x47, 140 | 'H' , 0x48, 141 | 'I' , 0x49, 142 | 'J' , 0x4A, 143 | 'K' , 0x4B, 144 | 'L' , 0x4C, 145 | 'M' , 0x4D, 146 | 'N' , 0x4E, 147 | 'O' , 0x4F, 148 | 'P' , 0x50, 149 | 'Q' , 0x51, 150 | 'R' , 0x52, 151 | 'S' , 0x53, 152 | 'T' , 0x54, 153 | 'U' , 0x55, 154 | 'V' , 0x56, 155 | 'W' , 0x57, 156 | 'X' , 0x58, 157 | 'Y' , 0x59, 158 | 'Z' , 0x5A, 159 | 0xC4, 0x5B, // LATIN CAPITAL LETTER A WITH DIAERESIS 160 | 0xD6, 0x5C, // LATIN CAPITAL LETTER O WITH DIAERESIS 161 | 0xD1, 0x5D, // LATIN CAPITAL LETTER N WITH TILDE 162 | 0xDC, 0x5E, // LATIN CAPITAL LETTER U WITH DIAERESIS 163 | 0xA7, 0x5F, // SECTION SIGN 164 | 0xBF, 0x60, // INVERTED QUESTION MARK 165 | 'a' , 0x61, // LATIN SMALL LETTER A...Z 166 | 'b' , 0x62, 167 | 'c' , 0x63, 168 | 'd' , 0x64, 169 | 'e' , 0x65, 170 | 'f' , 0x66, 171 | 'g' , 0x67, 172 | 'h' , 0x68, 173 | 'i' , 0x69, 174 | 'j' , 0x6A, 175 | 'k' , 0x6B, 176 | 'l' , 0x6C, 177 | 'm' , 0x6D, 178 | 'n' , 0x6E, 179 | 'o' , 0x6F, 180 | 'p' , 0x70, 181 | 'q' , 0x71, 182 | 'r' , 0x72, 183 | 's' , 0x73, 184 | 't' , 0x74, 185 | 'u' , 0x75, 186 | 'v' , 0x76, 187 | 'w' , 0x77, 188 | 'x' , 0x78, 189 | 'y' , 0x79, 190 | 'z' , 0x7A, 191 | 0xE4, 0x7B, // LATIN SMALL LETTER A WITH DIAERESIS 192 | 0xF6, 0x7C, // LATIN SMALL LETTER O WITH DIAERESIS 193 | 0xF1, 0x7D, // LATIN SMALL LETTER N WITH TILDE 194 | 0xFC, 0x7E, // LATIN SMALL LETTER U WITH DIAERESIS 195 | 0xE0, 0x7F, // LATIN SMALL LETTER A WITH GRAVE 196 | 197 | // Moved to the special char handling: 198 | // 0x60, 0x27, // GRAVE ACCENT 199 | // 0xE1, 0x61, // replacement for accented a 200 | // 0xED, 0x69, // replacement for accented i 201 | // 0xF3, 0x6F, // replacement for accented o 202 | // 0xFA, 0x75, // replacement for accented u 203 | 204 | 0 , 0 // End marker 205 | }; 206 | 207 | // Extended characters. In GSM they are preceeded by 0x1B. 208 | 209 | char ext_charset[] = { 0x0C, 0x0A, // 210 | '^' , 0x14, // CIRCUMFLEX ACCENT 211 | '{' , 0x28, // LEFT CURLY BRACKET 212 | '}' , 0x29, // RIGHT CURLY BRACKET 213 | '\\', 0x2F, // REVERSE SOLIDUS 214 | '[' , 0x3C, // LEFT SQUARE BRACKET 215 | '~' , 0x3D, // TILDE 216 | ']' , 0x3E, // RIGHT SQUARE BRACKET 217 | 0x7C, 0x40, // VERTICAL LINE 218 | 0xA4, 0x65, // EURO SIGN 219 | 0 , 0 // End marker 220 | }; 221 | 222 | 223 | // This table is used for outgoing (to GSM) conversion only: 224 | 225 | char iso_8859_15_chars[] = 226 | { 227 | 0x60, 0x27, // GRAVE ACCENT --> APOSTROPHE 228 | 0xA0, 0x20, // NO-BREAK SPACE --> SPACE 229 | 0xA2, 0x63, // CENT SIGN --> c 230 | 0xA6, 0x53, // LATIN CAPITAL LETTER S WITH CARON --> S 231 | 0xA8, 0x73, // LATIN SMALL LETTER S WITH CARON --> s 232 | 0xA9, 0x43, // COPYRIGHT SIGN --> C 233 | 0xAA, 0x61, // FEMININE ORDINAL INDICATOR --> a 234 | 0xAB, 0x3C, // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK --> < 235 | 0xAC, 0x2D, // NOT SIGN --> - 236 | 0xAD, 0x2D, // SOFT HYPHEN --> - 237 | 0xAE, 0x52, // REGISTERED SIGN --> R 238 | 0xAF, 0x2D, // MACRON --> - 239 | 0xB0, 0x6F, // DEGREE SIGN --> o 240 | 0xB1, 0x2B, // PLUS-MINUS SIGN --> + 241 | 0xB2, 0x32, // SUPERSCRIPT TWO --> 2 242 | 0xB3, 0x33, // SUPERSCRIPT THREE --> 3 243 | 0xB4, 0x5A, // LATIN CAPITAL LETTER Z WITH CARON --> Z 244 | 0xB5, 0x75, // MICRO SIGN --> u 245 | 0xB6, 0x49, // PILCROW SIGN --> I 246 | 0xB7, 0x2E, // MIDDLE DOT --> . 247 | 0xB8, 0x7A, // LATIN SMALL LETTER Z WITH CARON --> z 248 | 0xB9, 0x31, // SUPERSCRIPT ONE --> 1 249 | 0xBA, 0x6F, // MASCULINE ORDINAL INDICATOR --> o 250 | 0xBB, 0x3E, // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK --> > 251 | 0xBC, 0x4F, // LATIN CAPITAL LIGATURE OE --> O 252 | 0xBD, 0x6F, // LATIN SMALL LIGATURE OE --> o 253 | 0xBE, 0x59, // LATIN CAPITAL LETTER Y WITH DIAERESIS --> Y 254 | 0xC0, 0x41, // LATIN CAPITAL LETTER A WITH GRAVE --> A 255 | 0xC1, 0x41, // LATIN CAPITAL LETTER A WITH ACUTE --> A 256 | 0xC2, 0x41, // LATIN CAPITAL LETTER A WITH CIRCUMFLEX --> A 257 | 0xC3, 0x41, // LATIN CAPITAL LETTER A WITH TILDE --> A 258 | 0xC7, 0x09, // LATIN CAPITAL LETTER C WITH CEDILLA --> 0x09 (LATIN CAPITAL LETTER C WITH CEDILLA) 259 | 0xC8, 0x45, // LATIN CAPITAL LETTER E WITH GRAVE --> E 260 | 0xCA, 0x45, // LATIN CAPITAL LETTER E WITH CIRCUMFLEX --> E 261 | 0xCB, 0x45, // LATIN CAPITAL LETTER E WITH DIAERESIS --> E 262 | 0xCC, 0x49, // LATIN CAPITAL LETTER I WITH GRAVE --> I 263 | 0xCD, 0x49, // LATIN CAPITAL LETTER I WITH ACUTE --> I 264 | 0xCE, 0x49, // LATIN CAPITAL LETTER I WITH CIRCUMFLEX --> I 265 | 0xCF, 0x49, // LATIN CAPITAL LETTER I WITH DIAERESIS --> I 266 | 0xD0, 0x44, // LATIN CAPITAL LETTER ETH --> D 267 | 0xD2, 0x4F, // LATIN CAPITAL LETTER O WITH GRAVE --> O 268 | 0xD3, 0x4F, // LATIN CAPITAL LETTER O WITH ACUTE --> O 269 | 0xD4, 0x4F, // LATIN CAPITAL LETTER O WITH CIRCUMFLEX --> O 270 | 0xD5, 0x4F, // LATIN CAPITAL LETTER O WITH TILDE --> O 271 | 0xD7, 0x78, // MULTIPLICATION SIGN --> x 272 | 0xD9, 0x55, // LATIN CAPITAL LETTER U WITH GRAVE --> U 273 | 0xDA, 0x55, // LATIN CAPITAL LETTER U WITH ACUTE --> U 274 | 0xDB, 0x55, // LATIN CAPITAL LETTER U WITH CIRCUMFLEX --> U 275 | 0xDD, 0x59, // LATIN CAPITAL LETTER Y WITH ACUTE --> Y 276 | 0xDE, 0x62, // LATIN CAPITAL LETTER THORN --> b 277 | 0xE1, 0x61, // LATIN SMALL LETTER A WITH ACUTE --> a 278 | 0xE2, 0x61, // LATIN SMALL LETTER A WITH CIRCUMFLEX --> a 279 | 0xE3, 0x61, // LATIN SMALL LETTER A WITH TILDE --> a 280 | 0xE7, 0x09, // LATIN SMALL LETTER C WITH CEDILLA --> LATIN CAPITAL LETTER C WITH CEDILLA 281 | 0xEA, 0x65, // LATIN SMALL LETTER E WITH CIRCUMFLEX --> e 282 | 0xEB, 0x65, // LATIN SMALL LETTER E WITH DIAERESIS --> e 283 | 0xED, 0x69, // LATIN SMALL LETTER I WITH ACUTE --> i 284 | 0xEE, 0x69, // LATIN SMALL LETTER I WITH CIRCUMFLEX --> i 285 | 0xEF, 0x69, // LATIN SMALL LETTER I WITH DIAERESIS --> i 286 | 0xF0, 0x6F, // LATIN SMALL LETTER ETH --> o 287 | 0xF3, 0x6F, // LATIN SMALL LETTER O WITH ACUTE --> o 288 | 0xF4, 0x6F, // LATIN SMALL LETTER O WITH CIRCUMFLEX --> o 289 | 0xF5, 0x6F, // LATIN SMALL LETTER O WITH TILDE --> o 290 | 0xF7, 0x2F, // DIVISION SIGN --> / (SOLIDUS) 291 | 0xFA, 0x75, // LATIN SMALL LETTER U WITH ACUTE --> u 292 | 0xFB, 0x75, // LATIN SMALL LETTER U WITH CIRCUMFLEX --> u 293 | 0xFD, 0x79, // LATIN SMALL LETTER Y WITH ACUTE --> y 294 | 0xFE, 0x62, // LATIN SMALL LETTER THORN --> b 295 | 0xFF, 0x79, // LATIN SMALL LETTER Y WITH DIAERESIS --> y 296 | 297 | 0 , 0 298 | }; 299 | 300 | #ifdef USE_ICONV 301 | static iconv_t iconv4ucs; // UCS2->UTF8 descriptor 302 | static iconv_t iconv2ucs; // UTF8->UCS2 descriptor 303 | #endif 304 | 305 | int special_char2gsm(char ch, char *newch) 306 | { 307 | int table_row = 0; 308 | char *table = iso_8859_15_chars; 309 | 310 | while (table[table_row *2]) 311 | { 312 | if (table[table_row *2] == ch) 313 | { 314 | if (newch) 315 | *newch = table[table_row *2 +1]; 316 | return 1; 317 | } 318 | table_row++; 319 | } 320 | 321 | return 0; 322 | } 323 | 324 | // Return value: 325 | // 0 = ch not found. 326 | // 1 = ch found from normal table 327 | // 2 = ch found from extended table 328 | int char2gsm(char ch, char *newch) 329 | { 330 | int result = 0; 331 | int table_row; 332 | 333 | // search in normal translation table 334 | table_row=0; 335 | while (charset[table_row*2]) 336 | { 337 | if (charset[table_row*2] == ch) 338 | { 339 | if (newch) 340 | *newch = charset[table_row*2+1]; 341 | result = 1; 342 | break; 343 | } 344 | table_row++; 345 | } 346 | 347 | // if not found in normal table, then search in the extended table 348 | if (result == 0) 349 | { 350 | table_row=0; 351 | while (ext_charset[table_row*2]) 352 | { 353 | if (ext_charset[table_row*2] == ch) 354 | { 355 | if (newch) 356 | *newch = ext_charset[table_row*2+1]; 357 | result = 2; 358 | break; 359 | } 360 | table_row++; 361 | } 362 | } 363 | 364 | return result; 365 | } 366 | 367 | int gsm2char(char ch, char *newch, int which_table) 368 | { 369 | int table_row = 0; 370 | char *table; 371 | 372 | if (which_table == 1) 373 | table = charset; 374 | else if (which_table == 2) 375 | table = ext_charset; 376 | else 377 | return 0; 378 | 379 | while (table[table_row *2]) 380 | { 381 | if (table[table_row *2 +1] == ch) 382 | { 383 | *newch = table[table_row *2]; 384 | return 1; 385 | } 386 | table_row++; 387 | } 388 | 389 | return 0; 390 | } 391 | 392 | int iso_utf8_2gsm(char* source, int size, char* destination, int max) 393 | { 394 | int source_count=0; 395 | int dest_count=0; 396 | int found=0; 397 | char newch; 398 | char logtmp[51]; 399 | char tmpch; 400 | 401 | destination[dest_count]=0; 402 | if (source==0 || size <= 0) 403 | return 0; 404 | 405 | #ifdef DEBUGMSG 406 | log_charconv = 1; 407 | #endif 408 | 409 | if (log_charconv) 410 | { 411 | *logch_buffer = 0; 412 | logch("!! iso_utf8_2gsm(source=%.*s, size=%i)", size, source, size); 413 | logch(NULL); 414 | } 415 | 416 | // Convert each character until end of string 417 | while (source_count= max -2) 423 | break; 424 | destination[dest_count++] = 0x1B; 425 | } 426 | if (found >= 1) 427 | { 428 | destination[dest_count++] = newch; 429 | if (log_charconv) 430 | { 431 | sprintf(logtmp, "%02X[%c]", (unsigned char)source[source_count], prch(source[source_count])); 432 | if (found > 1 || source[source_count] != newch) 433 | { 434 | sprintf(strchr(logtmp, 0), "->%s%02X", (found == 2)? "Esc-" : "", (unsigned char)newch); 435 | if (gsm2char(newch, &tmpch, found)) 436 | sprintf(strchr(logtmp, 0), "[%c]", tmpch); 437 | } 438 | logch("%s ", logtmp); 439 | } 440 | } 441 | 442 | if (found == 0 && outgoing_utf8) 443 | { 444 | // ASCII and UTF-8 table: http://members.dslextreme.com/users/kkj/webtools/ascii_utf8_table.html 445 | // Good converter: http://www.macchiato.com/unicode/convert.html 446 | unsigned int c; 447 | int iterations = 0; 448 | // 3.1beta7: If UTF-8 decoded character is not found from tables, decoding is ignored: 449 | int saved_source_count = source_count; 450 | char sourcechars[51]; 451 | 452 | c = source[source_count]; 453 | if (log_charconv) 454 | sprintf(sourcechars, "%02X", (unsigned char)source[source_count]); 455 | 456 | // 3.1beta7: Check if there is enough characters left. 457 | // Following bytes in UTF-8 should begin with 10xx xxxx 458 | // which means 0x80 ... 0xBF 459 | if (((c & 0xFF) >= 0xC2 && (c & 0xFF) <= 0xC7) || 460 | ((c & 0xFF) >= 0xD0 && (c & 0xFF) <= 0xD7)) 461 | { 462 | if (source_count < size -1 && 463 | (source[source_count +1] & 0xC0) == 0x80) 464 | { 465 | // 110xxxxx 466 | c &= 0x1F; 467 | iterations = 1; 468 | } 469 | } 470 | else if ((c & 0xFF) >= 0xE0 && (c & 0xFF) <= 0xE7) 471 | { 472 | if (source_count < size -2 && 473 | (source[source_count +1] & 0xC0) == 0x80 && 474 | (source[source_count +2] & 0xC0) == 0x80) 475 | { 476 | // 1110xxxx 477 | c &= 0x0F; 478 | iterations = 2; 479 | } 480 | } 481 | else if ((c & 0xFF) >= 0xF0 && (c & 0xFF) <= 0xF4) 482 | { 483 | if (source_count < size -3 && 484 | (source[source_count +1] & 0xC0) == 0x80 && 485 | (source[source_count +2] & 0xC0) == 0x80 && 486 | (source[source_count +3] & 0xC0) == 0x80) 487 | { 488 | // 11110xxx 489 | c &= 0x07; 490 | iterations = 3; 491 | } 492 | } 493 | 494 | if (iterations > 0) 495 | { 496 | int i; 497 | 498 | for (i = 0; i < iterations; i++) 499 | { 500 | c = (c << 6) | (source[++source_count] -0x80); 501 | if (log_charconv) 502 | sprintf(strchr(sourcechars, 0), "%02X", (unsigned char)source[source_count]); 503 | } 504 | 505 | // Euro character is 20AC in UTF-8, but A4 in ISO-8859-15: 506 | if ((c & 0xFF) == 0xAC) 507 | c = 0xA4; 508 | 509 | found = char2gsm((char)c, &newch); 510 | if (found == 2) 511 | { 512 | if (dest_count >= max -2) 513 | break; 514 | destination[dest_count++] = 0x1B; 515 | } 516 | if (found >= 1) 517 | { 518 | destination[dest_count++] = newch; 519 | if (log_charconv) 520 | { 521 | sprintf(logtmp, "%s(%02X[%c])->%s%02X", sourcechars, (unsigned char)c, prch(c), 522 | (found == 2)? "Esc-" : "", (unsigned char)newch); 523 | if (gsm2char(newch, &tmpch, found)) 524 | sprintf(strchr(logtmp, 0), "[%c]", tmpch); 525 | logch("%s ", logtmp); 526 | } 527 | } 528 | else 529 | { 530 | found = special_char2gsm((char)c, &newch); 531 | if (found) 532 | { 533 | destination[dest_count++] = newch; 534 | if (log_charconv) 535 | { 536 | sprintf(logtmp, "%s(%02X[%c])~>%02X", sourcechars, (unsigned char)c, prch(c), (unsigned char)newch); 537 | if (gsm2char(newch, &tmpch, 1)) 538 | sprintf(strchr(logtmp, 0), "[%c]", tmpch); 539 | logch("%s ", logtmp); 540 | } 541 | } 542 | else 543 | source_count = saved_source_count; 544 | } 545 | } 546 | } 547 | 548 | // 3.1beta7: Try additional table: 549 | if (found == 0) 550 | { 551 | found = special_char2gsm(source[source_count], &newch); 552 | if (found) 553 | { 554 | destination[dest_count++] = newch; 555 | if (log_charconv) 556 | { 557 | sprintf(logtmp, "%02X[%c]~>%02X", (unsigned char)source[source_count], prch(source[source_count]), (unsigned char)newch); 558 | if (gsm2char(newch, &tmpch, 1)) 559 | sprintf(strchr(logtmp, 0), "[%c]", tmpch); 560 | logch("%s ", logtmp); 561 | } 562 | } 563 | } 564 | 565 | if (found==0) 566 | { 567 | writelogfile0(LOG_NOTICE, 0, 568 | tb_sprintf("Cannot convert %i. character %c 0x%2X to GSM, you might need to update the translation tables.", 569 | source_count +1, source[source_count], source[source_count])); 570 | #ifdef DEBUGMSG 571 | printf("%s\n", tb); 572 | #endif 573 | } 574 | 575 | source_count++; 576 | } 577 | 578 | if (log_charconv) 579 | logch(NULL); 580 | 581 | // Terminate destination string with 0, however 0x00 are also allowed within the string. 582 | destination[dest_count]=0; 583 | return dest_count; 584 | } 585 | 586 | // Outputs to the file. Return value: 0 = ok, -1 = error. 587 | int iso2utf8_file(FILE *fp, char *ascii, int userdatalength) 588 | { 589 | int result = 0; 590 | int idx; 591 | unsigned int c; 592 | char tmp[10]; 593 | int len; 594 | char logtmp[51]; 595 | int i; 596 | 597 | if (!fp || userdatalength < 0) 598 | return -1; 599 | 600 | #ifdef DEBUGMSG 601 | log_charconv = 1; 602 | #endif 603 | 604 | if (log_charconv) 605 | { 606 | *logch_buffer = 0; 607 | logch("!! iso2utf8_file(..., userdatalength=%i)", userdatalength); 608 | logch(NULL); 609 | } 610 | 611 | for (idx = 0; idx < userdatalength; idx++) 612 | { 613 | len = 0; 614 | c = ascii[idx] & 0xFF; 615 | // Euro character is 20AC in UTF-8, but A4 in ISO-8859-15: 616 | if (c == 0xA4) 617 | c = 0x20AC; 618 | 619 | if (c <= 0x7F) 620 | tmp[len++] = (char)c; 621 | else if (c <= 0x7FF) 622 | { 623 | tmp[len++] = (char)( 0xC0 | ((c >> 6) & 0x1F) ); 624 | tmp[len++] = (char)( 0x80 | (c & 0x3F) ); 625 | } 626 | else if (c <= 0x7FFF) // or <= 0xFFFF ? 627 | { 628 | tmp[len++] = (char)( 0xE0 | ((c >> 12) & 0x0F) ); 629 | tmp[len++] = (char)( 0x80 | ((c >> 6) & 0x3F) ); 630 | tmp[len++] = (char)( 0x80 | (c & 0x3F) ); 631 | } 632 | 633 | if (len == 0) 634 | { 635 | if (log_charconv) 636 | logch(NULL); 637 | writelogfile0(LOG_NOTICE, 0, 638 | tb_sprintf("UTF-8 conversion error with %i. ch 0x%2X %c.", idx +1, c, (char)c)); 639 | #ifdef DEBUGMSG 640 | printf("%s\n", tb); 641 | #endif 642 | } 643 | else 644 | { 645 | if (log_charconv) 646 | { 647 | sprintf(logtmp, "%02X[%c]", (unsigned char)ascii[idx], prch(ascii[idx])); 648 | if (len > 1 || ascii[idx] != tmp[0]) 649 | { 650 | strcat(logtmp, "->"); 651 | for (i = 0; i < len; i++) 652 | sprintf(strchr(logtmp, 0), "%02X", (unsigned char)tmp[i]); 653 | } 654 | logch("%s ", logtmp); 655 | } 656 | 657 | if (fwrite(tmp, 1, len, fp) != (size_t)len) 658 | { 659 | if (log_charconv) 660 | logch(NULL); 661 | writelogfile0(LOG_NOTICE, 0, tb_sprintf("Fatal file write error in UTF-8 conversion")); 662 | #ifdef DEBUGMSG 663 | printf("%s\n", tb); 664 | #endif 665 | result = -1; 666 | break; 667 | } 668 | } 669 | } 670 | 671 | if (log_charconv) 672 | logch(NULL); 673 | return result; 674 | } 675 | 676 | int gsm2iso(char* source, int size, char* destination, int max) 677 | { 678 | int source_count=0; 679 | int dest_count=0; 680 | char newch; 681 | 682 | if (source==0 || size==0) 683 | { 684 | destination[0]=0; 685 | return 0; 686 | } 687 | 688 | // Convert each character untl end of string 689 | while (source_count= 3) 785 | if (strcmp(text +strlen(text) -3, "20 ") == 0) 786 | flush = 1; 787 | // Incoming conversion: 788 | if (!flush) 789 | if (strlen(text) >= 6) 790 | if (strcmp(text +strlen(text) -6, "20[ ] ") == 0) 791 | flush = 1; 792 | // Line wrap after a reasonable length reached: 793 | if (!flush) 794 | if (strlen(logch_buffer) > 80) 795 | flush = 1; 796 | } 797 | #ifdef DEBUGMSG 798 | printf("%s", text); 799 | #endif 800 | } 801 | else 802 | flush = 1; 803 | 804 | if (flush) 805 | { 806 | if (*logch_buffer) 807 | writelogfile(LOG_DEBUG, 0, "charconv: %s", logch_buffer); 808 | *logch_buffer = 0; 809 | #ifdef DEBUGMSG 810 | printf("\n"); 811 | #endif 812 | } 813 | } 814 | 815 | char prch(char ch) 816 | { 817 | if ((unsigned char)ch >= ' ') 818 | return ch; 819 | return '.'; 820 | } 821 | 822 | //hdr -------------------------------------------------------------------------------- 823 | int iso2utf8( 824 | // 825 | // Converts to the buffer. Returns -1 in case of error, >= 0 = length of dest. 826 | // 827 | char *ascii, 828 | int userdatalength, 829 | size_t ascii_size 830 | ) 831 | { 832 | int result = 0; 833 | int idx; 834 | unsigned int c; 835 | char tmp[10]; 836 | int len; 837 | char logtmp[51]; 838 | int i; 839 | char *buffer; 840 | 841 | if (userdatalength < 0) 842 | return -1; 843 | 844 | if (!(buffer = (char *) malloc(ascii_size))) 845 | return -1; 846 | 847 | #ifdef DEBUGMSG 848 | log_charconv = 1; 849 | #endif 850 | 851 | if (log_charconv) 852 | { 853 | *logch_buffer = 0; 854 | logch("!! iso2utf8(..., userdatalength=%i)", userdatalength); 855 | logch(NULL); 856 | } 857 | 858 | for (idx = 0; idx < userdatalength; idx++) 859 | { 860 | len = 0; 861 | c = ascii[idx] & 0xFF; 862 | // Euro character is 20AC in UTF-8, but A4 in ISO-8859-15: 863 | if (c == 0xA4) 864 | c = 0x20AC; 865 | 866 | if (c <= 0x7F) 867 | tmp[len++] = (char) c; 868 | else if (c <= 0x7FF) 869 | { 870 | tmp[len++] = (char) (0xC0 | ((c >> 6) & 0x1F)); 871 | tmp[len++] = (char) (0x80 | (c & 0x3F)); 872 | } 873 | else if (c <= 0x7FFF) // or <= 0xFFFF ? 874 | { 875 | tmp[len++] = (char) (0xE0 | ((c >> 12) & 0x0F)); 876 | tmp[len++] = (char) (0x80 | ((c >> 6) & 0x3F)); 877 | tmp[len++] = (char) (0x80 | (c & 0x3F)); 878 | } 879 | 880 | if (len == 0) 881 | { 882 | if (log_charconv) 883 | logch(NULL); 884 | writelogfile0(LOG_NOTICE, 0, tb_sprintf("UTF-8 conversion error with %i. ch 0x%2X %c.", idx + 1, c, (char) c)); 885 | #ifdef DEBUGMSG 886 | printf("%s\n", tb); 887 | #endif 888 | } 889 | else 890 | { 891 | if (log_charconv) 892 | { 893 | sprintf(logtmp, "%02X[%c]", (unsigned char) ascii[idx], prch(ascii[idx])); 894 | if (len > 1 || ascii[idx] != tmp[0]) 895 | { 896 | strcat(logtmp, "->"); 897 | for (i = 0; i < len; i++) 898 | sprintf(strchr(logtmp, 0), "%02X", (unsigned char) tmp[i]); 899 | } 900 | logch("%s ", logtmp); 901 | } 902 | 903 | if ((size_t) (result + len) < ascii_size - 1) 904 | { 905 | strncpy(buffer + result, tmp, len); 906 | result += len; 907 | } 908 | else 909 | { 910 | if (log_charconv) 911 | logch(NULL); 912 | writelogfile0(LOG_NOTICE, 0, tb_sprintf("Fatal error (buffer too small) in UTF-8 conversion")); 913 | #ifdef DEBUGMSG 914 | printf("%s\n", tb); 915 | #endif 916 | result = -1; 917 | break; 918 | } 919 | } 920 | } 921 | 922 | if (log_charconv) 923 | logch(NULL); 924 | 925 | if (result >= 0) 926 | { 927 | memcpy(ascii, buffer, result); 928 | ascii[result] = 0; 929 | } 930 | 931 | free(buffer); 932 | 933 | return result; 934 | } 935 | 936 | //hdr -------------------------------------------------------------------------------- 937 | int encode_7bit_packed( 938 | // 939 | // Encodes a string to GSM 7bit (USSD) packed format. 940 | // Returns number of septets. 941 | // Handles padding as defined on GSM 03.38 version 5.6.1 (ETS 300 900) page 17. 942 | // 943 | char *text, 944 | char *dest, 945 | size_t size_dest 946 | ) 947 | { 948 | int len; 949 | int i; 950 | char buffer[512]; 951 | char buffer2[512]; 952 | char padding = '\r'; 953 | 954 | //len = iso_utf8_2gsm(text, strlen(text), buffer2, sizeof(buffer2), ALPHABET_AUTO, 0); 955 | len = iso_utf8_2gsm(text, strlen(text), buffer2, sizeof(buffer2)); 956 | 957 | #ifdef DEBUGMSG 958 | printf("characters: %i\n", len); 959 | printf("characters %% 8: %i\n", len % 8); 960 | #endif 961 | 962 | if ((len % 8 == 7) || (len % 8 == 0 && len && buffer2[len - 1] == padding)) 963 | { 964 | if ((size_t) len < sizeof(buffer2) - 1) 965 | { 966 | buffer2[len++] = padding; 967 | #ifdef DEBUGMSG 968 | printf("adding padding, characters: %i\n", len); 969 | #endif 970 | } 971 | } 972 | 973 | i = text2pdu(buffer2, len, buffer, 0); 974 | snprintf(dest, size_dest, "%s", buffer); 975 | 976 | #ifdef DEBUGMSG 977 | printf("octets: %i\n", strlen(buffer) / 2); 978 | for (len = 0; buffer[len]; len += 2) 979 | printf("%.2s ", buffer + len); 980 | printf("\n"); 981 | #endif 982 | return i; 983 | } 984 | 985 | //hdr -------------------------------------------------------------------------------- 986 | int decode_7bit_packed( 987 | // 988 | // Decodes GSM 7bit (USSD) packed string. 989 | // Returns length of dest. -1 in the case or error and "ERROR" in dest. 990 | // Handles padding as defined on GSM 03.38 version 5.6.1 (ETS 300 900) page 17. 991 | // 992 | char *text, 993 | char *dest, 994 | size_t size_dest 995 | ) 996 | { 997 | int len; 998 | int i; 999 | char buffer[512]; 1000 | char buffer2[512]; 1001 | char *p; 1002 | int septets; 1003 | int padding = '\r'; 1004 | 1005 | snprintf(buffer, sizeof(buffer), "%s", text); 1006 | while ((p = strchr(buffer, ' '))) 1007 | strcpyo(p, p + 1); 1008 | for (i = 0; buffer[i]; i++) 1009 | buffer[i] = toupper((int) buffer[i]); 1010 | 1011 | i = strlen(buffer); 1012 | if (i % 2) 1013 | { 1014 | snprintf(dest, size_dest, "ERROR"); 1015 | return -1; 1016 | } 1017 | 1018 | septets = i / 2 * 8 / 7; 1019 | snprintf(buffer2, sizeof(buffer2), "%02X%s", septets, buffer); 1020 | 1021 | #ifdef DEBUGMSG 1022 | printf("septets: %i (0x%02X)\n", septets, septets); 1023 | printf("septets %% 8: %i\n", septets % 8); 1024 | printf("%s\n", buffer2); 1025 | #endif 1026 | memset(buffer, 0, sizeof(buffer)); 1027 | pdu2text(buffer2, buffer, &len, 0, 0, 0, 0, 0); 1028 | 1029 | if ((septets % 8 == 0 && len && buffer[len - 1] == padding) || (septets % 8 == 1 && len > 1 && buffer[len - 1] == padding && buffer[len - 2] == padding)) 1030 | { 1031 | len--; 1032 | #ifdef DEBUGMSG 1033 | printf("removing padding, characters: %i\n", len); 1034 | #endif 1035 | } 1036 | 1037 | i = gsm2iso(buffer, len, buffer2, sizeof(buffer2)); 1038 | if (incoming_utf8) 1039 | i = iso2utf8(buffer2, i, sizeof(buffer2)); 1040 | snprintf(dest, size_dest, "%s", buffer2); 1041 | 1042 | return i; 1043 | } 1044 | 1045 | // ****************************************************************************** 1046 | 1047 | #ifdef USE_ICONV 1048 | int iconv_init(void) 1049 | { 1050 | // do noy use 'UTF8' alias - it not supported in cygwin/mingw 1051 | return (iconv4ucs = iconv_open("UTF-8", "UCS-2BE")) != (iconv_t)-1 1052 | && (iconv2ucs = iconv_open("UCS-2BE", "UTF-8")) != (iconv_t)-1; 1053 | } 1054 | 1055 | static size_t iconv_convert(iconv_t cd, char *buf, size_t* ilen, size_t maxlen) 1056 | { 1057 | char tmp[MAXTEXT], *out, *in; 1058 | size_t olen, rc; 1059 | const char* err; 1060 | 1061 | if (!maxlen || !ilen || !*ilen || !buf) 1062 | return 0; 1063 | 1064 | // reset conversion descriptor 1065 | iconv(cd, NULL, NULL, NULL, NULL); 1066 | err = NULL; 1067 | in = buf; 1068 | out = tmp; 1069 | olen = sizeof(tmp); 1070 | rc = iconv(cd, &in, ilen, &out, &olen); 1071 | if (rc == (size_t)-1) 1072 | { 1073 | switch (errno) 1074 | { 1075 | case E2BIG: 1076 | err = "Buffer to small"; 1077 | break; 1078 | case EILSEQ: 1079 | err = "Invalid sequnce"; 1080 | break; 1081 | case EINVAL: 1082 | err = "Incomplete sequence"; 1083 | break; 1084 | default: 1085 | err = strerror(errno); 1086 | break; 1087 | } 1088 | } 1089 | olen = sizeof(tmp) - olen; 1090 | if (olen >= maxlen) 1091 | { 1092 | err = "output buffer too small"; 1093 | olen = maxlen; 1094 | } 1095 | memcpy(buf, tmp, olen); 1096 | if (err != NULL) 1097 | writelogfile(LOG_NOTICE, 0, "Unicode conversion error: %s", err); 1098 | return olen; 1099 | } 1100 | 1101 | size_t iconv_utf2ucs(char *buf, size_t len, size_t maxlen) 1102 | { 1103 | return iconv_convert(iconv2ucs, buf, &len, maxlen); 1104 | } 1105 | 1106 | size_t iconv_ucs2utf(char *buf, size_t len, size_t maxlen) 1107 | { 1108 | return iconv_convert(iconv4ucs, buf, &len, maxlen); 1109 | } 1110 | 1111 | size_t iconv_ucs2utf_chk(char *buf, size_t len, size_t maxlen) 1112 | { 1113 | size_t olen, ilen = len; 1114 | olen = iconv_convert(iconv4ucs, buf, &ilen, maxlen); 1115 | buf[olen] = 0; 1116 | if (ilen != 0) 1117 | ilen = (len - ilen + 1) / 2; 1118 | return ilen; 1119 | } 1120 | 1121 | int is_ascii_gsm(char* buf, size_t len) 1122 | { 1123 | char tmp; 1124 | size_t i; 1125 | for (i = 0; i < len; i++) 1126 | if (buf[i] < ' ' || char2gsm(buf[i], &tmp) != 1) 1127 | return 0; 1128 | return 1; 1129 | } 1130 | #endif // USE_ICONV 1131 | -------------------------------------------------------------------------------- /src/pdu/charset.h: -------------------------------------------------------------------------------- 1 | /* 2 | SMS Server Tools 3 3 | Copyright (C) 2006- Keijo Kasvi 4 | http://smstools3.kekekasvi.com/ 5 | 6 | Based on SMS Server Tools 2 from Stefan Frings 7 | http://www.meinemullemaus.de/ 8 | SMS Server Tools version 2 and below are Copyright (C) Stefan Frings. 9 | 10 | This program is free software unless you got it under another license directly 11 | from the author. You can redistribute it and/or modify it under the terms of 12 | the GNU General Public License as published by the Free Software Foundation. 13 | Either version 2 of the License, or (at your option) any later version. 14 | */ 15 | 16 | #ifndef CHARSET_H 17 | #define CHARSET_H 18 | 19 | char logch_buffer[8192]; 20 | 21 | // Logging is not used externally, but it's placed to the end of source file. 22 | void logch(char* format, ...); 23 | char prch(char ch); 24 | 25 | // Both functions return the size of the converted string 26 | // max limits the number of characters to be written into 27 | // destination 28 | // size is the size of the source string 29 | // max is the maximum size of the destination string 30 | // The GSM character set contains 0x00 as a valid character 31 | 32 | int gsm2iso(char* source, int size, char* destination, int max); 33 | 34 | int iso_utf8_2gsm(char* source, int size, char* destination, int max); 35 | int iso2utf8_file(FILE *fp, char *ascii, int userdatalength); 36 | 37 | //int iso2gsm(char* source, int size, char* destination, int max); 38 | 39 | //int unicode2sms(char* source, int size, char* destination, int max); 40 | 41 | int decode_7bit_packed(char *text, char *dest, size_t size_dest); 42 | int encode_7bit_packed(char *text, char *dest, size_t size_dest); 43 | 44 | #ifndef USE_ICONV 45 | int decode_ucs2(char *buffer, int len); 46 | #else 47 | int iconv_init(void); 48 | size_t iconv_utf2ucs(char* buf, size_t len, size_t maxlen); 49 | size_t iconv_ucs2utf(char* buf, size_t len, size_t maxlen); 50 | size_t iconv_ucs2utf_chk(char *buf, size_t len, size_t maxlen); 51 | int is_ascii_gsm(char* buf, size_t len); 52 | #endif 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /src/pdu/compat.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "compat.h" 8 | 9 | char *strcpyo(char *dest, const char *src) 10 | { 11 | size_t i; 12 | 13 | for (i = 0; src[i] != '\0'; i++) 14 | dest[i] = src[i]; 15 | 16 | dest[i] = '\0'; 17 | 18 | return dest; 19 | } 20 | 21 | void writelogfile (int severity, int trouble, const char *fmt, ...) { 22 | va_list ap; 23 | va_start(ap, fmt); 24 | vfprintf(stderr, fmt, ap); 25 | va_end(ap); 26 | } 27 | 28 | void writelogfile0 (int severity, int trouble, char *text) { 29 | writelogfile(severity, trouble, "%s", text); 30 | } 31 | 32 | char *tb_sprintf (char* format, ...) { 33 | va_list argp; 34 | 35 | va_start(argp, format); 36 | vsnprintf(tb, sizeof(tb), format, argp); 37 | va_end(argp); 38 | 39 | return tb; 40 | } 41 | 42 | -------------------------------------------------------------------------------- /src/pdu/compat.h: -------------------------------------------------------------------------------- 1 | // Compatibility file for pdu.c diverge 2 | 3 | #include 4 | 5 | // Maxmum size of a single sms, can be 160/140 or less 6 | #define maxsms_pdu 160 7 | #define maxsms_ucs2 140 8 | #define maxsms_binary 140 9 | 10 | // Sizes for some buffers: 11 | #define SIZE_TO 100 12 | #define SIZE_SMSC 100 13 | #define SIZE_UDH_DATA 500 14 | #define SIZE_UDH_TYPE 4096 15 | #define SIZE_TB 1024 16 | #define MAXTEXT 39016 17 | #define LENGTH_PDU_DETAIL_REC 70 18 | 19 | #define isdigitc(ch) isdigit((int)(ch)) 20 | 21 | char international_prefixes[PATH_MAX +1]; 22 | char national_prefixes[PATH_MAX +1]; 23 | int validity_period; // Validity period for messages. 24 | int log_charconv; // 1 if character set conversion is logged. 25 | int incoming_utf8; // 1 if incoming files are saved using UTF-8 character set. 26 | int outgoing_utf8; // 1 if outgoing files are automatically converted from UTF-8 to ISO and GSM. 27 | 28 | // Text buffer for error messages: 29 | char tb[SIZE_TB]; 30 | 31 | // compat.c függvényei 32 | char *strcpyo(char *dest, const char *src); 33 | void writelogfile (int severity, int trouble, const char *fmt, ...); 34 | void writelogfile0 (int severity, int trouble, char *text); 35 | char *tb_sprintf (char* format, ...); 36 | 37 | -------------------------------------------------------------------------------- /src/pdu/pdu.h: -------------------------------------------------------------------------------- 1 | /* 2 | SMS Server Tools 3 3 | Copyright (C) 2006- Keijo Kasvi 4 | http://smstools3.kekekasvi.com/ 5 | 6 | Based on SMS Server Tools 2 from Stefan Frings 7 | http://www.meinemullemaus.de/ 8 | SMS Server Tools version 2 and below are Copyright (C) Stefan Frings. 9 | 10 | This program is free software unless you got it under another license directly 11 | from the author. You can redistribute it and/or modify it under the terms of 12 | the GNU General Public License as published by the Free Software Foundation. 13 | Either version 2 of the License, or (at your option) any later version. 14 | */ 15 | 16 | #ifndef PDU_H 17 | #define PDU_H 18 | 19 | #define SIZE_WARNING_HEADERS 4096 20 | 21 | //Alphabet values: -1=GSM 0=ISO 1=binary 2=UCS2 22 | #define ALPHABET_GSM -1 23 | #define ALPHABET_ISO 0 24 | #define ALPHABET_BINARY 1 25 | #define ALPHABET_UCS2 2 26 | 27 | #define NF_UNKNOWN 129 28 | #define NF_INTERNATIONAL 145 29 | #define NF_NATIONAL 161 30 | 31 | int set_numberformat(int *numberformat, char *number, int number_type); 32 | 33 | // Make the PDU string from a mesage text and destination phone number. 34 | // The destination variable pdu has to be big enough. 35 | // alphabet indicates the character set of the message. 36 | // flash_sms enables the flash flag. 37 | // mode select the pdu version (old or new). 38 | // if udh is true, then udh_data contains the optional user data header in hex dump, example: "05 00 03 AF 02 01" 39 | void make_pdu(const char* number, char* message, int messagelen, int alphabet, int flash_sms, int report, int udh, 40 | char* udh_data, char* mode, char* pdu, int validity, int replace_msg, int system_msg, int number_type, char *smsc); 41 | 42 | // Splits a PDU string into the parts 43 | // Input: 44 | // pdu is the pdu string 45 | // mode can be old or new and selects the pdu version 46 | // Output: 47 | // alphabet indicates the character set of the message. 48 | // sendr Sender 49 | // date and time Date/Time-stamp 50 | // message is the message text or binary message 51 | // smsc that sent this message 52 | // with_udh returns the udh flag of the message 53 | // is_statusreport is 1 if this was a status report 54 | // is_unsupported_pdu is 1 if this pdu was not supported 55 | // udh return the udh as hex dump 56 | // Returns the length of the message 57 | int splitpdu(char *pdu, char *mode, int *alphabet, char *sendr, char *date, char *time, char *message, 58 | char *smsc, int *with_udh, char *a_udh_data, char *a_udh_type, int *is_statusreport, 59 | int *is_unsupported_pdu, char *from_toa, int *report, int *replace, char *warning_headers, 60 | int *flash, int bin_udh); 61 | 62 | int octet2bin(char* octet); 63 | int octet2bin_check(char* octet); 64 | int isXdigit(char ch); 65 | 66 | // Returns a length of udh (including UDHL), -1 if error. 67 | // pdu is 0-terminated ascii(hex) pdu string with 68 | // or without spaces. 69 | int explain_udh(char *udh_type, char *pdu); 70 | 71 | // Return value: -1 = error, 0 = not found. 72 | // 1 = found 8bit, 2 = found 16bit. 73 | // udh must be in header format, "05 00 03 02 03 02 " 74 | int get_remove_concatenation(char *udh, int *message_id, int *parts, int *part); 75 | int get_concatenation(char *udh, int *message_id, int *parts, int *part); 76 | int remove_concatenation(char *udh); 77 | 78 | int explain_toa(char *dest, char *octet_char, int octet_int); 79 | 80 | void explain_status(char *dest, size_t size_dest, int status); 81 | 82 | int get_pdu_details(char *dest, size_t size_dest, char *pdu, int mnumber, char *mode); 83 | void sort_pdu_details(char *dest); 84 | 85 | int pdu2text(char *pdu, char *text, int *text_length, int *expected_length, 86 | int with_udh, char *udh, char *udh_type, int *errorpos); 87 | int text2pdu(char* text, int length, char* pdu, char* udh); 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /src/pdu/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "pdu.h" 8 | 9 | int main () { 10 | // Make the PDU string from a mesage text and destination phone number. 11 | // The destination variable pdu has to be big enough. 12 | // alphabet indicates the character set of the message. 13 | // flash_sms enables the flash flag. 14 | // mode select the pdu version (old or new). 15 | // if udh is true, then udh_data contains the optional user data header in hex dump, example: "05 00 03 AF 02 01" 16 | 17 | // void make_pdu( 18 | // char* number, char* message, int messagelen, int alphabet, int 19 | // flash_sms, int report, int udh, char* udh_data, char* mode, char* pdu, 20 | // int validity, int replace_msg, int system_msg, int number_type, char 21 | // *smsc 22 | // ); 23 | 24 | 25 | char pdu[4096]; 26 | 27 | char *phone_number = "06709405300"; 28 | char *message = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyzzzzzzzzzzmmmmmmmmmm"; 29 | 30 | make_pdu( 31 | phone_number, 32 | message, 33 | strlen(message), 34 | ALPHABET_GSM, 35 | 0, // flash 36 | 1, // report 37 | 0, // udh 38 | NULL, // udh_data 39 | "new", // mode ("old" vagy "new") 40 | pdu, 41 | 0, // validity 42 | 0, // replace_msg 43 | 0, // system_msg (0 vagy 1 vagy 2) 44 | 0, // number_type 45 | NULL // char *smsc 46 | ); 47 | 48 | printf("%s\n", pdu); 49 | 50 | return 0; 51 | } 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/phone_number_validator.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "defaults.h" 12 | #include "conf.h" 13 | #include "debug.h" 14 | #include "pnv.h" 15 | 16 | 17 | static int phone_number_validation (const char *phone_number) { 18 | // új pnv objektum. A locale környezeti változókból megállapítja az 19 | // alapértelmezett régiót (default-locale). Ez felülírható a config által. 20 | pnv_t *pnv = pnv_new(phone_number); 21 | 22 | // Ha a configban meg van adva, akkor a default-locale alapján beállítjuk a 23 | // pnv alapértelmezett régióját, ami a helyi formátumú számokhoz kell. 24 | if (strlen(conf_root()->default_locale)) 25 | pnv_set_locale(pnv, conf_root()->default_locale); 26 | 27 | // validálás 28 | switch (pnv_validate(pnv)) { 29 | case PNV_OK: 30 | printf("Phone number validation OK\n"); 31 | printf("Phone number after convert: %s\n", pnv_get_phone_number_converted(pnv)); 32 | printf("Provider: %s %s\n", pnv_get_msg_ok(pnv), pnv_get_locale(pnv)); 33 | return 0; 34 | 35 | case PNV_FAIL: 36 | printf("Phone number validation FAIL\n"); 37 | printf("FAILED: %s\n", pnv_get_msg_fail(pnv)); 38 | return -1; 39 | 40 | case PNV_UNKNOWN: 41 | printf("Phone number maybe not valid: %s\n", pnv_get_msg_fail(pnv)); 42 | if (conf_root()->pnv == CONF_PNV_FORCE) { 43 | printf("FAILED: %s\n", pnv_get_msg_fail(pnv)); 44 | return -1; 45 | } else { 46 | return 0; 47 | } 48 | } 49 | 50 | return 0; 51 | } 52 | 53 | int main (int argc, char *argv[]) { 54 | // config file set to default 55 | conf_set_config_file(DEFAULT_CONFIG_FILE); 56 | 57 | // config file set to SMSAMI_CONFIG environment variable, if exists 58 | if (getenv("SMSAMI_CONFIG")) 59 | conf_set_config_file(getenv("SMSAMI_CONFIG")); 60 | 61 | if (conf_load()) 62 | return 1; 63 | 64 | if (argc < 2) { 65 | printf("usage: phone_number_validator \n"); 66 | return -1; 67 | } 68 | 69 | return phone_number_validation(argv[1]); 70 | } 71 | 72 | -------------------------------------------------------------------------------- /src/pnv.c: -------------------------------------------------------------------------------- 1 | /* pnv.c - Phone Number Validation 2 | * Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu 3 | * 4 | * This program is free software, distributed under the terms of 5 | * the GNU General Public License Version 2. See the LICENSE file 6 | * at the top of the source tree. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "debug.h" 18 | #include "misc.h" 19 | #include "pnv.h" 20 | 21 | int pnv_hu_local (pnv_t *pnv); 22 | int pnv_hu_international (pnv_t *pnv); 23 | 24 | // TODO: http://countrycode.org/ (a magyar kódok nem teljesen jók) 25 | 26 | // A táblázatban a több jegyű országkódoknak feljebb kell lenniük, mint a 27 | // kevesebb jegyű országkódoknak. Azért, mert a kereső függvény föntről lefele 28 | // halad és az első illeszkedésnél befejezi a vizsgálatot. 29 | struct pnv_table { 30 | char *iso; 31 | char *code; 32 | char *country; 33 | int (*local)(pnv_t*); 34 | int (*international)(pnv_t*); 35 | } pnv_table[] = { 36 | { .iso = "HU", 37 | .code = "36", 38 | .country = "Hungary", 39 | .local = pnv_hu_local, 40 | .international = pnv_hu_international, 41 | }, 42 | { .iso = "DE", 43 | .code = "49", 44 | .country = "Germany", 45 | .local = NULL, 46 | .international = NULL, 47 | }, 48 | }; 49 | 50 | int foreach_pnv_table_i; // FOREACH_PNV_TABLE belső használatára 51 | // BUG: nem teljesen korrekt a globális változó volta 52 | // miatt. Ha egymásba ágyazott FOREACH_PNV_TABLE ciklusokat 53 | // használunk, akkor a két iteráció ugyan azt a 54 | // foreach_pnv_table_i változót fogja használni, ami anomáliához 55 | // vezet. 56 | 57 | // FOREACH_PNV_TABLE (s) 58 | // Végigmegy az pnv_table struktúra tömbön, és a soron következő struktúra címét 59 | // átadja a paraméterül kapott "s" mutatónak. Segfault veszély van, ha a 60 | // cikluson kívül megpróbáljuk használni az s mutató területét, ugyanis az egy 61 | // nemlétező tömbelemre fog mutatni. 62 | #define FOREACH_PNV_TABLE(s) \ 63 | for (foreach_pnv_table_i = 0, s = &pnv_table[0]; \ 64 | foreach_pnv_table_i < (sizeof(pnv_table) / sizeof(struct pnv_table)); \ 65 | foreach_pnv_table_i++, s = &pnv_table[foreach_pnv_table_i]) 66 | 67 | #define ZONES_NUM (sizeof(zones) / sizeof(struct zone)) 68 | 69 | // pnv->phone_number_converted stringbe ír. Átlapolás lehetséges, mert az pnv->tmp 70 | // ideiglenes string-et használja 71 | #define PNV_PRINTF(...) \ 72 | do { \ 73 | snprintf(pnv->tmp, sizeof(pnv->tmp), __VA_ARGS__); \ 74 | scpy(pnv->phone_number_converted, pnv->tmp); \ 75 | } while (0) 76 | 77 | #define PNV_RETURN_FAIL(s) \ 78 | do { \ 79 | if ((s) && strlen(s)) \ 80 | pnv->result_msg_fail = (s); \ 81 | return PNV_FAIL; \ 82 | } while (0) 83 | 84 | #define PNV_RETURN_UNKNOWN(s) \ 85 | do { \ 86 | if ((s) && strlen(s)) \ 87 | pnv->result_msg_fail = (s); \ 88 | return PNV_UNKNOWN; \ 89 | } while (0) 90 | 91 | #define PNV_RETURN_OK(s) \ 92 | do { \ 93 | if ((s) && strlen(s)) \ 94 | pnv->result_msg_ok = (s); \ 95 | return PNV_OK; \ 96 | } while (0) 97 | 98 | #define PNV_LEN (strlen(pnv->phone_number_converted)) 99 | 100 | #define PNV_DELCHARS(s) strdelchars(pnv->phone_number_converted, (s)) 101 | 102 | #define PNV_IF_REGEX(r) if (! _pnv_regex_func((r), pnv->phone_number_converted)) 103 | static int _pnv_regex_func (const char *regular_expression, const char *pattern) { 104 | regex_t regex; 105 | int regret; 106 | 107 | if (regcomp(®ex, regular_expression, REG_EXTENDED)) { 108 | fprintf(stderr, "Could not compile regex: \"%s\"\n", regular_expression); 109 | return -1; 110 | } 111 | 112 | regret = regexec(®ex, pattern, 0, NULL, 0); 113 | if (regret && regret != REG_NOMATCH) { 114 | char error_message[128]; 115 | regerror(regret, ®ex, error_message, sizeof(error_message)); 116 | fprintf(stderr, "Regex match failed: %s\n", error_message); 117 | } 118 | 119 | regfree(®ex); 120 | return regret; 121 | } 122 | 123 | const char *pnv_state_str (enum pnv_state state) { 124 | switch (state) { 125 | case PNV_OK: return "PNV_OK"; 126 | case PNV_FAIL: return "PNV_FAIL"; 127 | case PNV_UNKNOWN: return "PNV_UNKNOWN"; 128 | default: return "???"; 129 | } 130 | } 131 | 132 | int pnv_hu_local (pnv_t *pnv) { 133 | // szóköz, kötőjel, vessző és pont karakterek kivágása 134 | PNV_DELCHARS(" -,./"); 135 | 136 | // 06 helyett +36 137 | PNV_IF_REGEX("^06") { 138 | PNV_PRINTF("+36%s", &pnv->phone_number_converted[2]); 139 | } 140 | 141 | // 36 helyett +36 142 | PNV_IF_REGEX("^36") { 143 | PNV_PRINTF("+%s", &pnv->phone_number_converted[0]); 144 | } 145 | 146 | if (PNV_LEN == 9) { 147 | // 20....... és 70....... helyett +3620 és +3670 148 | PNV_IF_REGEX("^[27]0.......$") { 149 | PNV_PRINTF("+36%s", &pnv->phone_number_converted[0]); 150 | } 151 | 152 | // 30....... és 31....... helyett +3630 és +3631 153 | PNV_IF_REGEX("^3[01].......$") { 154 | PNV_PRINTF("+36%s", &pnv->phone_number_converted[0]); 155 | } 156 | } 157 | 158 | // 00 helyett + 159 | PNV_IF_REGEX("^00") { 160 | PNV_PRINTF("+%s", &pnv->phone_number_converted[2]); 161 | } 162 | 163 | PNV_IF_REGEX("^12..$") PNV_RETURN_OK("SMS szolgálatás"); 164 | PNV_IF_REGEX("^17..$") PNV_RETURN_FAIL("emeltdíjas"); 165 | 166 | // hibás szám formátum 167 | if (pnv->phone_number_converted[0] != '+') { 168 | PNV_RETURN_FAIL("hibás szám vagy körzet"); 169 | } 170 | 171 | PNV_RETURN_OK(""); 172 | } 173 | 174 | int pnv_hu_international (pnv_t *pnv) { 175 | // szóköz, kötőjel, vessző és pont karakterek kivágása 176 | PNV_DELCHARS(" -,./"); 177 | 178 | // legalább 6 jegyűnek (a + karaktert is beleértve) kell lennie a 179 | // telefonszámnak 180 | if (PNV_LEN < 6) 181 | PNV_RETURN_FAIL("kevés számjegy"); 182 | 183 | PNV_IF_REGEX("^\\+3640") PNV_RETURN_FAIL("kék szám"); 184 | PNV_IF_REGEX("^\\+3651") PNV_RETURN_FAIL("internetszolgáltató"); 185 | PNV_IF_REGEX("^\\+3660") PNV_RETURN_FAIL("szolgáltató megszűnt"); 186 | PNV_IF_REGEX("^\\+3680") PNV_RETURN_FAIL("zöld szám"); 187 | PNV_IF_REGEX("^\\+3681") PNV_RETURN_FAIL("emeltdíjas"); 188 | PNV_IF_REGEX("^\\+3690") PNV_RETURN_FAIL("emeltdíjas"); 189 | PNV_IF_REGEX("^\\+3691") PNV_RETURN_FAIL("emeltdíjas"); 190 | 191 | PNV_IF_REGEX("^\\+3620") { 192 | if (PNV_LEN == 12) 193 | PNV_RETURN_OK("Telenor"); 194 | else 195 | PNV_RETURN_FAIL("kevés vagy sok számjegy"); 196 | } 197 | 198 | PNV_IF_REGEX("^\\+3630") { 199 | if (PNV_LEN == 12) 200 | PNV_RETURN_OK("T-Mobile"); 201 | else 202 | PNV_RETURN_FAIL("kevés vagy sok számjegy"); 203 | } 204 | 205 | PNV_IF_REGEX("^\\+3631") { 206 | if (PNV_LEN == 12) 207 | PNV_RETURN_OK("Tesco mobile"); 208 | else 209 | PNV_RETURN_FAIL("kevés vagy sok számjegy"); 210 | } 211 | 212 | PNV_IF_REGEX("^\\+3670") { 213 | if (PNV_LEN == 12) 214 | PNV_RETURN_OK("Vodafone"); 215 | else 216 | PNV_RETURN_FAIL("kevés vagy sok számjegy"); 217 | } 218 | 219 | PNV_RETURN_FAIL("nem mobil körzet"); 220 | } 221 | 222 | #define PNV_DEBUG(...) 223 | // #define PNV_DEBUG(...) printf(__VA_ARGS__) 224 | enum pnv_state pnv_validate (pnv_t *pnv) { 225 | struct pnv_table *row; 226 | int ret = 0; 227 | 228 | trim(pnv->phone_number_converted); 229 | 230 | // ha nincs + a szám elején, akkor helyi (local) számozást kell vizsgálnunk 231 | if (pnv->phone_number_converted[0] != '+') { 232 | 233 | // ha nincs beállítva a helyi locale, akkor nem tudjuk ellenőrizni a 234 | // helyi számozást, és konvertálni sem tudjuk nemzetközi formátumra 235 | if (!strlen(pnv->locale)) { 236 | PNV_RETURN_UNKNOWN("helyi számot nem lehet vizsgálni ismeretlen régióban"); 237 | } 238 | 239 | // megkeressük a helyi régióhoz tartozó local() függvényt 240 | FOREACH_PNV_TABLE (row) { 241 | if (row->iso && !strcmp(pnv->locale, row->iso)) { 242 | pnv->result_country = row->country; 243 | PNV_DEBUG("Found country: %s\n", row->country); 244 | 245 | // ha be van állítva a local() függvény 246 | if (row->local) { 247 | ret = row->local(pnv); // callback hívás 248 | PNV_DEBUG("local() returned %s\n", ret_str(ret)); 249 | } 250 | break; 251 | } 252 | } 253 | } 254 | 255 | if (ret) { 256 | PNV_DEBUG("result_msg_fail: %s\n", pnv->result_msg_fail); 257 | return ret; 258 | } 259 | 260 | // ha továbbra sincs nemzetközi formátumunk, akkor az azért lehet, mert a 261 | // local() függvény egy olyan szolgáltatás számát engedélyezte, ami csak 262 | // belföldről elérhető (pl. 12xx SMS számok) 263 | if (pnv->phone_number_converted[0] != '+') { 264 | if (!pnv->result_msg_ok || !strlen(pnv->result_msg_ok)) { 265 | PNV_RETURN_FAIL("belső hiba, a local() függvény OK-ot adott vissza, de nem írt a result_msg_ok -ba, és nem is konvertálta át a telefonszámot nemzetközi (+) formátumba!"); 266 | } 267 | return ret; 268 | } 269 | 270 | // ezen a ponton a local() függvény jóváhagyta és átkonvertálta a 271 | // telefonszámot "+" -al kezdődő nemzetközi formátumúvá. 272 | 273 | if (PNV_LEN < 4) 274 | PNV_RETURN_FAIL("kevés számjegy"); 275 | 276 | // ha az első + karakteren kívül van másik + karakter is benne 277 | if (index(&pnv->phone_number_converted[1], '+')) 278 | PNV_RETURN_FAIL("túl sok + karakter"); 279 | 280 | // 0-val nem kezdődik országkód. Ez kiszűri az esetleges félreütéseket, 281 | // mint pl. "000" a magyar formátumban. 282 | PNV_IF_REGEX("^\\+0") { 283 | PNV_RETURN_FAIL("rossz nemzetközi formátum"); 284 | } 285 | 286 | ret = PNV_UNKNOWN; 287 | 288 | FOREACH_PNV_TABLE (row) { 289 | if (row->code && !strncmp(&pnv->phone_number_converted[1], row->code, strlen(row->code))) { 290 | pnv->result_country = row->country; 291 | PNV_DEBUG("Found country: %s\n", row->country); 292 | 293 | // ha be van állítva a local() függvény 294 | if (row->international) { 295 | ret = row->international(pnv); // callback hívás 296 | PNV_DEBUG("international() returned %s\n", ret_str(ret)); 297 | } 298 | break; 299 | } 300 | } 301 | 302 | if (ret == PNV_UNKNOWN) { 303 | PNV_RETURN_UNKNOWN("országkód nem található"); 304 | } 305 | 306 | return ret; 307 | } 308 | 309 | static void determine_locale (pnv_t *pnv) { 310 | char *locale; 311 | locale = setlocale(LC_TELEPHONE, ""); 312 | 313 | if (!locale || !strcmp(locale, "C") || !strcmp(locale, "POSIX")) 314 | return; 315 | 316 | pnv_set_locale(pnv, locale); 317 | } 318 | 319 | pnv_t *pnv_new (const char *phone_number_converted) { 320 | pnv_t *pnv = malloc(sizeof(*pnv)); 321 | if (pnv == NULL) { 322 | fprintf(stderr, "pnv_new(): out of memory"); 323 | return NULL; 324 | } 325 | memset(pnv, 0, sizeof(*pnv)); 326 | 327 | scpy(pnv->phone_number_converted, phone_number_converted); 328 | scpy(pnv->phone_number_original, phone_number_converted); 329 | determine_locale(pnv); 330 | 331 | return pnv; 332 | } 333 | 334 | void pnv_destroy (pnv_t *pnv) { 335 | if (pnv) 336 | free(pnv); 337 | 338 | return; 339 | } 340 | 341 | const char *pnv_get_country (pnv_t *pnv) { 342 | if (!pnv) 343 | return NULL; 344 | return pnv->result_country; 345 | } 346 | 347 | // TODO: ezt talán jobb lenne pnv_get_provider -re átnevezni 348 | const char *pnv_get_msg_ok (pnv_t *pnv) { 349 | if (!pnv) 350 | return NULL; 351 | return pnv->result_msg_ok; 352 | } 353 | 354 | const char *pnv_get_msg_fail (pnv_t *pnv) { 355 | if (!pnv) 356 | return NULL; 357 | return pnv->result_msg_fail; 358 | } 359 | 360 | const char *pnv_get_phone_number_converted (pnv_t *pnv) { 361 | if (!pnv) 362 | return NULL; 363 | return pnv->phone_number_converted; 364 | } 365 | 366 | const char *pnv_get_phone_number_original (pnv_t *pnv) { 367 | if (!pnv) 368 | return NULL; 369 | return pnv->phone_number_original; 370 | } 371 | 372 | void pnv_set_locale (pnv_t *pnv, const char *locale) { 373 | if (!pnv) 374 | return; 375 | 376 | if (!locale || !strlen(locale)) { 377 | pnv->locale[0] = '\0'; 378 | return; 379 | } 380 | 381 | if (strlen(locale) >= 2) { 382 | pnv->locale[0] = toupper(locale[0]); 383 | pnv->locale[1] = toupper(locale[1]); 384 | pnv->locale[2] = '\0'; 385 | } 386 | } 387 | 388 | const char *pnv_get_locale (pnv_t *pnv) { 389 | if (!pnv) 390 | return NULL; 391 | return pnv->locale; 392 | } 393 | 394 | -------------------------------------------------------------------------------- /src/pnv.h: -------------------------------------------------------------------------------- 1 | /* pnv.h - Phone Number Validation 2 | * Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu 3 | * 4 | * This program is free software, distributed under the terms of 5 | * the GNU General Public License Version 2. See the LICENSE file 6 | * at the top of the source tree. 7 | */ 8 | 9 | enum pnv_state { 10 | PNV_OK = 0, 11 | PNV_FAIL = -1, 12 | PNV_UNKNOWN = -2, 13 | }; 14 | 15 | typedef struct pnv_t { 16 | char tmp[128]; // string temp, makrók használják 17 | char phone_number_original[128]; // paraméterben megadott telefonszám kerül ide 18 | char phone_number_converted[128]; // ide is, de a regexpek itt fognak vele játszani 19 | char locale[8]; // pl. HU vagy DE 20 | char *result_country; 21 | char *result_iso_code; 22 | char *result_msg_ok; 23 | char *result_msg_fail; 24 | } pnv_t; 25 | 26 | 27 | pnv_t *pnv_new (const char *phone_number); 28 | void pnv_destroy (pnv_t *pnv); 29 | enum pnv_state pnv_validate (pnv_t *pnv); 30 | const char *pnv_state_str (enum pnv_state state); 31 | const char *pnv_get_country (pnv_t *pnv); 32 | const char *pnv_get_msg_ok (pnv_t *pnv); 33 | const char *pnv_get_msg_fail (pnv_t *pnv); 34 | const char *pnv_get_phone_number_converted (pnv_t *pnv); 35 | const char *pnv_get_phone_number_original (pnv_t *pnv); 36 | void pnv_set_locale (pnv_t *pnv, const char *locale); 37 | const char *pnv_get_locale (pnv_t *pnv); 38 | 39 | -------------------------------------------------------------------------------- /src/pnv_json.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "defaults.h" 12 | #include "debug.h" 13 | #include "pnv.h" 14 | 15 | static int phone_number_validation (const char *phone_number, const char *locale) { 16 | // új pnv objektum. A locale környezeti változókból megállapítja az 17 | // alapértelmezett régiót (default-locale). Ez felülírható a config által. 18 | pnv_t *pnv = pnv_new(phone_number); 19 | 20 | if (locale) 21 | pnv_set_locale(pnv, locale); 22 | 23 | // validálás 24 | int state = pnv_validate(pnv); 25 | 26 | printf("{" 27 | "\"state\": \"%s\", " 28 | "\"provider\": \"%s\", " 29 | "\"invalidMsg\": \"%s\", " 30 | "\"revisedNumber\": \"%s\"}\n" 31 | , pnv_state_str(state) 32 | , (state) ? "" : pnv_get_msg_ok(pnv) 33 | , (state) ? pnv_get_msg_fail(pnv) : "" 34 | , (state) ? "" : pnv_get_phone_number_converted(pnv) 35 | ); 36 | 37 | return 0; 38 | } 39 | 40 | int main (int argc, char *argv[]) { 41 | char *phone_number = NULL; 42 | char *locale = NULL; 43 | 44 | if (argc < 2) { 45 | printf("JSS AMISMS Phone Number Validator utility\n"); 46 | printf("usage: pnv_json [locale]\n"); 47 | return -1; 48 | } 49 | 50 | phone_number = argv[1]; 51 | 52 | // ha van második param 53 | if (argc > 2) 54 | locale = argv[2]; 55 | 56 | return phone_number_validation(phone_number, locale); 57 | } 58 | 59 | -------------------------------------------------------------------------------- /src/sms.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "defaults.h" 12 | #include "ami.h" 13 | #include "conf.h" 14 | #include "pdu/pdu.h" 15 | #include "debug.h" 16 | #include "misc.h" 17 | #include "pnv.h" 18 | #include "verbose.h" 19 | #include "sms.h" 20 | 21 | #ifdef DEBUG 22 | #define CON_DEBUG 23 | #endif 24 | #include "logger.h" 25 | 26 | #include "option.h" 27 | 28 | /*****************************************************************************/ 29 | 30 | ami_t *ami; 31 | pnv_t *pnv = NULL; 32 | const char *selected_device = NULL; 33 | 34 | /*****************************************************************************/ 35 | 36 | void quit (int exitcode, const char *fmt, ...) { 37 | if (pnv) 38 | pnv_destroy(pnv); 39 | 40 | if (fmt) { 41 | va_list ap; 42 | va_start(ap, fmt); 43 | vprintf(fmt, ap); 44 | va_end(ap); 45 | } 46 | 47 | exit(exitcode); 48 | } 49 | 50 | static void event_donglesmsstatus (ami_event_t *event) { 51 | const char *status = ami_getvar(event, "Status"); 52 | verbosef(3, "DongleSMSStatus (ID=%s, Status=%s)\n", ami_getvar(event, "ID"), status); 53 | 54 | if (!strcmp(status, "Sent")) { 55 | verbosef(1, "Sent OK\n"); 56 | quit(0, NULL); 57 | } else { 58 | quit(EXIT_ERR, "FAILED: not sent: %s\n", status); 59 | } 60 | } 61 | 62 | // returns 0 if the given message is eg. "[dongle0] SMS queued for send" 63 | static int check_sms_queued_for_send_message (char *given_message) { 64 | char required_message[96]; 65 | // example: "[dongle0] SMS queued for send" 66 | snprintf( 67 | required_message, 68 | sizeof(required_message), 69 | "[%s] SMS queued for send", 70 | conf_device(selected_device)->dongle 71 | ); 72 | 73 | if (given_message == NULL) 74 | return -1; 75 | 76 | return strcmp(given_message, required_message); 77 | } 78 | 79 | static void response_donglesendpdu (ami_event_t *event) { 80 | const char *id = ami_getvar(event, "ID"); 81 | verbosef(3, "Received DongleSendPDU response (ID=%s, Message=%s\n", id, ami_getvar(event, "Message")); 82 | 83 | if (check_sms_queued_for_send_message(ami_getvar(event, "Message"))) { 84 | quit(EXIT_AGAIN, "FAILED: %s\n", ami_getvar(event, "Message")); 85 | } 86 | 87 | verbosef(3, "Registering event DongleSMSStatus with ID %s\n", id); 88 | ami_event_register(ami, event_donglesmsstatus, NULL, 89 | "Event: DongleSMSStatus\n" 90 | "ID: %s\n" 91 | , id 92 | ); 93 | verbosef(1, "Sending ...\n"); 94 | } 95 | 96 | void repair_at_sign (char *string, int size) { 97 | int i; 98 | for (i = 0; i < size; i++) { 99 | if (string[i] == '@') { 100 | string[i] = 0; // @ character code in GSM 03.38 Character Set 101 | // reference: https://www.csoft.co.uk/support/character-sets 102 | } 103 | } 104 | } 105 | 106 | void convert_and_send_sms () { 107 | int alphabet; 108 | 109 | // ISO-8859-2 formátumban kell tolni neki. Wapmonsteren minden látszik, kalapos ő és ű ékezetek (max. 140 char). 110 | // alphabet = ALPHABET_BINARY; 111 | 112 | // Ékezetek helyett krix-krax (max 160 char). 113 | alphabet = ALPHABET_GSM; 114 | 115 | // ugyan olyan, mint az ALPHABET_GSM 116 | // alphabet = ALPHABET_ISO; 117 | 118 | // wide-char (max 70 char). 119 | // alphabet = ALPHABET_UCS2; 120 | 121 | int report = 0; 122 | int with_udh = 0; 123 | char *udh_data = ""; 124 | 125 | verbosef(3, "phone_number = \"%s\", message_text=\"%s\"\n", pnv_get_phone_number_converted(pnv), option.message_text); 126 | char pdu[4096]; 127 | 128 | int message_length = strlen(option.message_text); 129 | 130 | // @ character repair 131 | repair_at_sign(option.message_text, message_length); 132 | 133 | make_pdu( 134 | pnv_get_phone_number_converted(pnv), option.message_text, message_length, alphabet, option.flash, 135 | report, with_udh, udh_data, "new", pdu, 1440, 0, 0, 1, NULL); 136 | 137 | verbosef(3, "Sending DongleSendPDU action with PDU \"%s\"\n", pdu); 138 | ami_action(ami, response_donglesendpdu, NULL, 139 | "Action: DongleSendPDU\n" 140 | "Device: %s\n" 141 | "PDU: %s\n" 142 | , conf_device(selected_device)->dongle 143 | , pdu 144 | ); 145 | } 146 | 147 | void event_disconnect (ami_event_t *event) { 148 | quit(EXIT_AGAIN, "FAILED: %s %s(%s:%s): %s\n", 149 | (!strcmp(ami_getvar(event, "WasAuthenticated"), "1")) ? "Disconnected from" : "Can't connect to", 150 | ami_getvar(event, "Host"), 151 | ami_getvar(event, "IP"), 152 | ami_getvar(event, "Port"), 153 | ami_getvar(event, "Reason")); 154 | } 155 | 156 | void event_connect (ami_event_t *event) { 157 | verbosef(3, "Connected to %s(%s:%s)\n", 158 | ami_getvar(event, "Host"), 159 | ami_getvar(event, "IP"), 160 | ami_getvar(event, "Port")); 161 | 162 | convert_and_send_sms(); 163 | } 164 | 165 | int phone_number_validation () { 166 | // --nopnv kapcsoló esetén nem ellenőrizzük a telefonszámot 167 | if (option.nopnv || conf_root()->pnv == CONF_PNV_OFF) 168 | return 0; 169 | 170 | // új pnv objektum. A locale környezeti változókból megállapítja az 171 | // alapértelmezett régiót (default-locale). Ez felülírható a config által. 172 | pnv = pnv_new(option.phone_number); 173 | 174 | // Ha a configban meg van adva, akkor a default-locale alapján beállítjuk a 175 | // pnv alapértelmezett régióját, ami a helyi formátumú számokhoz kell. 176 | if (strlen(conf_root()->default_locale)) 177 | pnv_set_locale(pnv, conf_root()->default_locale); 178 | 179 | // validálás 180 | switch (pnv_validate(pnv)) { 181 | case PNV_OK: 182 | verbosef(2, "Phone number validation OK\n"); 183 | verbosef(2, "Phone number after convert: %s\n", pnv_get_phone_number_converted(pnv)); 184 | verbosef(2, "Provider: %s %s\n", pnv_get_msg_ok(pnv), pnv_get_locale(pnv)); 185 | return 0; 186 | 187 | case PNV_FAIL: 188 | verbosef(2, "Phone number validation FAIL\n"); 189 | quit(EXIT_ERR, "FAILED: %s\n", pnv_get_msg_fail(pnv)); 190 | 191 | case PNV_UNKNOWN: 192 | verbosef(2, "Phone number maybe not valid: %s\n", pnv_get_msg_fail(pnv)); 193 | if (conf_root()->pnv == CONF_PNV_FORCE) { 194 | printf("FAILED: %s\n", pnv_get_msg_fail(pnv)); 195 | return -1; 196 | } else { 197 | return 0; 198 | } 199 | } 200 | 201 | return 0; 202 | } 203 | 204 | void got_sigalrm (int signum) { 205 | quit(EXIT_AGAIN, "FAILED: timeout\n"); 206 | } 207 | 208 | int main (int argc, char *argv[]) { 209 | // absolute timeout 210 | alarm(35); 211 | signal(SIGALRM, got_sigalrm); 212 | 213 | // parse command-line arguments and save to option structure (in option.h) 214 | option_parse_args(argc, argv); 215 | 216 | // config file set to default 217 | conf_set_config_file(DEFAULT_CONFIG_FILE); 218 | 219 | // config file set to AMISMS_CONFIG environment variable, if exists 220 | if (getenv("AMISMS_CONFIG")) 221 | conf_set_config_file(getenv("AMISMS_CONFIG")); 222 | 223 | // config file set to argument, if specified --config option 224 | if (strlen(option.config)) 225 | conf_set_config_file(option.config); 226 | 227 | // 160 karakternél levágjuk a stringet. Ez majd megszűnik, ha rendben lesz 228 | // a PDU és a multi-part SMS 229 | option.message_text[160] = '\0'; 230 | 231 | if (conf_load()) 232 | return 1; 233 | 234 | if (phone_number_validation()) 235 | return -1; 236 | 237 | if (selected_device == NULL) 238 | selected_device = conf_root()->default_device_name; 239 | 240 | if (!conf_device_exists(selected_device)) { 241 | printf("Device \"%s\" not found in configuration!\n", selected_device); 242 | return 1; 243 | } 244 | 245 | ami = ami_new(EV_DEFAULT); 246 | if (ami == NULL) { 247 | printf("ami_new() returned NULL"); 248 | return 1; 249 | } 250 | 251 | ami_credentials(ami, 252 | conf_device(selected_device)->username, 253 | conf_device(selected_device)->password, 254 | conf_device(selected_device)->host, 255 | conf_device(selected_device)->port 256 | ); 257 | 258 | ami_event_register(ami, event_disconnect, NULL, "Disconnect"); 259 | ami_event_register(ami, event_connect, NULL, "Connect"); 260 | verbosef(3, "Connecting to Asterisk ...\n"); 261 | ami_connect(ami); 262 | 263 | ev_loop(EV_DEFAULT, 0); 264 | 265 | ami_destroy(ami); 266 | return 0; 267 | } 268 | 269 | -------------------------------------------------------------------------------- /src/sms.h: -------------------------------------------------------------------------------- 1 | /* sms.h 2 | * Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu 3 | * 4 | * This program is free software, distributed under the terms of 5 | * the GNU General Public License Version 2. See the LICENSE file 6 | * at the top of the source tree. 7 | */ 8 | 9 | #define EXIT_OK 0 // SMS rendben elküldve, nincs hiba 10 | #define EXIT_ERR 1 // hiba, az SMS nem küldhető el 11 | #define EXIT_AGAIN 2 // az SMS nem lett elküldve, de lehet, hogy legközelebb sikerül 12 | 13 | -------------------------------------------------------------------------------- /src/verbose.c: -------------------------------------------------------------------------------- 1 | /* verbose.c 2 | * Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu 3 | * 4 | * This program is free software, distributed under the terms of 5 | * the GNU General Public License Version 2. See the LICENSE file 6 | * at the top of the source tree. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "logger.h" 16 | 17 | int verbose_level = 0; 18 | 19 | void verbosef (int level, const char *fmt, ...) { 20 | if (level > verbose_level) 21 | return; 22 | 23 | va_list ap; 24 | va_start(ap, fmt); 25 | vprintf(fmt, ap); 26 | va_end(ap); 27 | } 28 | 29 | void verbose_inc_level () { 30 | verbose_level++; 31 | } 32 | 33 | -------------------------------------------------------------------------------- /src/verbose.h: -------------------------------------------------------------------------------- 1 | /* verbose.h 2 | * Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu 3 | * 4 | * This program is free software, distributed under the terms of 5 | * the GNU General Public License Version 2. See the LICENSE file 6 | * at the top of the source tree. 7 | */ 8 | 9 | void verbosef (int level, const char *fmt, ...); 10 | void verbose_inc_level (); 11 | 12 | -------------------------------------------------------------------------------- /t/.gitignore: -------------------------------------------------------------------------------- 1 | testlist 2 | test*/output 3 | test*/test 4 | test*/test.o 5 | test*/*.o 6 | -------------------------------------------------------------------------------- /t/Makefile: -------------------------------------------------------------------------------- 1 | # test top-dir Makefile 2 | 3 | MAKEFLAGS += --no-print-directory 4 | TESTS := $(shell find -maxdepth 1 -type d -name 'test*' | sort -n) 5 | 6 | .PHONY: all clean 7 | .DEFAULT: all 8 | 9 | all: compile run check_output 10 | 11 | compile: 12 | echo $(TESTS) >testlist 13 | @for dir in ${TESTS}; do ( cd $$dir && ${MAKE} all ); done 14 | 15 | run: 16 | @chmod 600 */data/test.ini 17 | @for dir in ${TESTS}; do ( cd $$dir && ${MAKE} run ); done 18 | 19 | check_output: 20 | ./check_output.sh 21 | 22 | clean: 23 | @for dir in ${TESTS}; do ( cd $$dir && ${MAKE} clean ); done 24 | @rm -f testlist 25 | 26 | -------------------------------------------------------------------------------- /t/check_output.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | export LANG=C 5 | 6 | if [ ! -e testlist ]; then 7 | echo "testlist not found, please use make" 8 | exit 255 9 | fi 10 | 11 | echo "" 12 | for t in `cat testlist`; do 13 | t=$(basename $t) 14 | cd $t 15 | echo -n "Checking $t...: " 16 | 17 | if diff -q output.ok output; then 18 | echo "OK" 19 | cd .. 20 | continue 21 | fi 22 | 23 | diff -u output.ok output || true 24 | echo "" 25 | echo "*** $t FAILED [$(cat README)]" 26 | echo "" 27 | cd .. 28 | exit 255 29 | done 30 | 31 | echo "" 32 | echo "....... All's well, ends well ....... ;-)" 33 | echo "" 34 | -------------------------------------------------------------------------------- /t/test02/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -Wall 3 | CFLAGS += -I../../libamievent 4 | CFLAGS += -I../../libamievent/libc-jss 5 | CFLAGS += -I../../src/pdu 6 | CFLAGS += -I../../src 7 | CFLAGS += -ggdb 8 | LDFLAGS = -lev -lm 9 | 10 | OBJ += ../../libamievent/libc-jss/ini/ini.o 11 | OBJ += ../../src/conf.o 12 | PROGS=test 13 | 14 | .PHONY: all run clean 15 | all: $(patsubst %, %.o, $(PROGS)) $(OBJ) $(PROGS) 16 | 17 | %: %.o $(OBJ) 18 | gcc $(CFLAGS) -o $@ $< $(OBJ) $(LDFLAGS) 19 | 20 | clean: 21 | rm -f *.o $(OBJ) $(PROGS) 22 | 23 | run: 24 | ./test >output 25 | -------------------------------------------------------------------------------- /t/test02/README: -------------------------------------------------------------------------------- 1 | Config file loading with no errors 2 | -------------------------------------------------------------------------------- /t/test02/data/test.ini: -------------------------------------------------------------------------------- 1 | ; AMISMS example configuration 2 | 3 | ; force phone number checking 4 | pnv = on 5 | 6 | ; default region for local format phone number checking 7 | default-locale = hu 8 | 9 | ; default device to use 10 | default = device0 11 | 12 | [device0] 13 | host = localhost ; hostname or IP address where Asterisk is running 14 | port = 5038 ; port where AMI is listening, see asterisk/manager.conf 15 | username = amiuser ; name of Asterisk Manager user which have flag 'call' 16 | password = amipassword ; plaintext password for AMI user 17 | dongle = dongle0 ; dongle name in asterisk/chan_dongle.conf 18 | 19 | [another-mobile-provider] 20 | host = 21 | port = 5038 22 | username = asterisk_ami_user 23 | password = asterisk_ami_password 24 | dongle = dongle0 25 | 26 | ; vim:ft=dosini: 27 | -------------------------------------------------------------------------------- /t/test02/output.ok: -------------------------------------------------------------------------------- 1 | default = device0 2 | pnv = 0 3 | default-locale = hu 4 | 5 | device0 6 | host = localhost 7 | port = 5038 8 | username = amiuser 9 | password = amipassword 10 | dongle = dongle0 11 | another-mobile-provider 12 | host = localhost 13 | port = 5038 14 | username = asterisk_ami_user 15 | password = asterisk_ami_password 16 | dongle = dongle0 17 | -------------------------------------------------------------------------------- /t/test02/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "conf.h" 6 | 7 | int main (int argc, char *argv[]) { 8 | conf_set_config_file("data/test.ini"); 9 | 10 | if (conf_load()) 11 | return 0; 12 | 13 | conf_dump(); 14 | return 0; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /t/test03/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -Wall 3 | CFLAGS += -I../../libamievent 4 | CFLAGS += -I../../libamievent/libc-jss 5 | CFLAGS += -I../../src/pdu 6 | CFLAGS += -I../../src 7 | CFLAGS += -ggdb 8 | LDFLAGS = -lev -lm 9 | 10 | OBJ += ../../libamievent/libc-jss/ini/ini.o 11 | OBJ += ../../src/conf.o 12 | PROGS=test 13 | 14 | .PHONY: all run clean 15 | all: $(patsubst %, %.o, $(PROGS)) $(OBJ) $(PROGS) 16 | 17 | %: %.o $(OBJ) 18 | gcc $(CFLAGS) -o $@ $< $(OBJ) $(LDFLAGS) 19 | 20 | clean: 21 | rm -f *.o $(OBJ) $(PROGS) 22 | 23 | run: 24 | ./test >output 25 | -------------------------------------------------------------------------------- /t/test03/README: -------------------------------------------------------------------------------- 1 | Config file check with unknown option name 2 | -------------------------------------------------------------------------------- /t/test03/data/test.ini: -------------------------------------------------------------------------------- 1 | default = device0 2 | 3 | [device0] 4 | host = localhost ; hostname or IP address where Asterisk is running 5 | port = 5038 ; port where AMI is listening, see asterisk/manager.conf 6 | username = amiuser ; name of Asterisk Manager user which have flag 'call' 7 | password = amipassword ; plaintext password for AMI user 8 | dongle = dongle0 ; dongle name in asterisk/chan_dongle.conf 9 | invalidname = invalidvalue 10 | 11 | [another-mobile-provider] 12 | host = 13 | port = 5038 14 | username = asterisk_ami_user 15 | password = asterisk_ami_password 16 | dongle = dongle0 17 | 18 | -------------------------------------------------------------------------------- /t/test03/output.ok: -------------------------------------------------------------------------------- 1 | unknown config option "invalidname" in section "device0" 2 | Parse error in config file: data/test.ini 3 | -------------------------------------------------------------------------------- /t/test03/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "conf.h" 6 | 7 | int main (int argc, char *argv[]) { 8 | conf_set_config_file("data/test.ini"); 9 | 10 | if (conf_load()) 11 | return 0; 12 | 13 | conf_dump(); 14 | return 0; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /t/test04/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -Wall 3 | CFLAGS += -I../../libamievent 4 | CFLAGS += -I../../libamievent/libc-jss 5 | CFLAGS += -I../../src/pdu 6 | CFLAGS += -I../../src 7 | CFLAGS += -ggdb 8 | LDFLAGS = -lev -lm 9 | 10 | OBJ += ../../libamievent/libc-jss/ini/ini.o 11 | OBJ += ../../src/conf.o 12 | PROGS=test 13 | 14 | .PHONY: all run clean 15 | all: $(patsubst %, %.o, $(PROGS)) $(OBJ) $(PROGS) 16 | 17 | %: %.o $(OBJ) 18 | gcc $(CFLAGS) -o $@ $< $(OBJ) $(LDFLAGS) 19 | 20 | clean: 21 | rm -f *.o $(OBJ) $(PROGS) 22 | 23 | run: 24 | ./test >output 25 | -------------------------------------------------------------------------------- /t/test04/README: -------------------------------------------------------------------------------- 1 | Config file check with missing options 2 | -------------------------------------------------------------------------------- /t/test04/data/test.ini: -------------------------------------------------------------------------------- 1 | default = device0 2 | 3 | [device0] 4 | port = 9999; 5 | -------------------------------------------------------------------------------- /t/test04/output.ok: -------------------------------------------------------------------------------- 1 | no username defined in device "device0" 2 | no password defined in device "device0" 3 | no dongle defined in device "device0" 4 | Parse error in config file: data/test.ini 5 | -------------------------------------------------------------------------------- /t/test04/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "conf.h" 6 | 7 | int main (int argc, char *argv[]) { 8 | conf_set_config_file("data/test.ini"); 9 | 10 | if (conf_load()) 11 | return 0; 12 | 13 | conf_dump(); 14 | return 0; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /t/test05/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -Wall 3 | CFLAGS += -I../../libamievent 4 | CFLAGS += -I../../libamievent/libc-jss 5 | CFLAGS += -I../../src/pdu 6 | CFLAGS += -I../../src 7 | CFLAGS += -ggdb 8 | LDFLAGS = -lev -lm 9 | 10 | OBJ += ../../libamievent/libc-jss/ini/ini.o 11 | OBJ += ../../src/conf.o 12 | PROGS=test 13 | 14 | .PHONY: all run clean 15 | all: $(patsubst %, %.o, $(PROGS)) $(OBJ) $(PROGS) 16 | 17 | %: %.o $(OBJ) 18 | gcc $(CFLAGS) -o $@ $< $(OBJ) $(LDFLAGS) 19 | 20 | clean: 21 | rm -f *.o $(OBJ) $(PROGS) 22 | 23 | run: 24 | ./test >output 25 | -------------------------------------------------------------------------------- /t/test05/README: -------------------------------------------------------------------------------- 1 | Config file check with missing device section 2 | -------------------------------------------------------------------------------- /t/test05/data/test.ini: -------------------------------------------------------------------------------- 1 | default = device0 2 | -------------------------------------------------------------------------------- /t/test05/output.ok: -------------------------------------------------------------------------------- 1 | At least one device must be specified in configuration! 2 | Default device not found: "device0" 3 | Parse error in config file: data/test.ini 4 | -------------------------------------------------------------------------------- /t/test05/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "conf.h" 6 | 7 | int main (int argc, char *argv[]) { 8 | conf_set_config_file("data/test.ini"); 9 | 10 | if (conf_load()) 11 | return 0; 12 | 13 | conf_dump(); 14 | return 0; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /t/test06/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -Wall 3 | CFLAGS += -I../../libamievent 4 | CFLAGS += -I../../libamievent/libc-jss 5 | CFLAGS += -I../../src/pdu 6 | CFLAGS += -I../../src 7 | CFLAGS += -ggdb 8 | LDFLAGS = -lev -lm 9 | 10 | OBJ += ../../libamievent/libc-jss/ini/ini.o 11 | OBJ += ../../src/conf.o 12 | PROGS=test 13 | 14 | .PHONY: all run clean 15 | all: $(patsubst %, %.o, $(PROGS)) $(OBJ) $(PROGS) 16 | 17 | %: %.o $(OBJ) 18 | gcc $(CFLAGS) -o $@ $< $(OBJ) $(LDFLAGS) 19 | 20 | clean: 21 | rm -f *.o $(OBJ) $(PROGS) 22 | 23 | run: 24 | ./test >output 25 | -------------------------------------------------------------------------------- /t/test06/README: -------------------------------------------------------------------------------- 1 | Config file check with non-existent default device 2 | -------------------------------------------------------------------------------- /t/test06/data/test.ini: -------------------------------------------------------------------------------- 1 | default = nonexistentdevice 2 | 3 | [device0] 4 | host = localhost ; hostname or IP address where Asterisk is running 5 | port = 5038 ; port where AMI is listening, see asterisk/manager.conf 6 | username = amiuser ; name of Asterisk Manager user which have flag 'call' 7 | password = amipassword ; plaintext password for AMI user 8 | dongle = dongle0 ; dongle name in asterisk/chan_dongle.conf 9 | 10 | [another-mobile-provider] 11 | host = 12 | port = 5038 13 | username = asterisk_ami_user 14 | password = asterisk_ami_password 15 | dongle = dongle0 16 | 17 | -------------------------------------------------------------------------------- /t/test06/output.ok: -------------------------------------------------------------------------------- 1 | Default device not found: "nonexistentdevice" 2 | Parse error in config file: data/test.ini 3 | -------------------------------------------------------------------------------- /t/test06/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "conf.h" 6 | 7 | int main (int argc, char *argv[]) { 8 | conf_set_config_file("data/test.ini"); 9 | 10 | if (conf_load()) 11 | return 0; 12 | 13 | conf_dump(); 14 | return 0; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /t/test07/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -Wall 3 | CFLAGS += -I../../libamievent 4 | CFLAGS += -I../../libamievent/libc-jss 5 | CFLAGS += -I../../src/pdu 6 | CFLAGS += -I../../src 7 | CFLAGS += -ggdb 8 | LDFLAGS = -lev -lm 9 | 10 | OBJ += ../../libamievent/libc-jss/ini/ini.o 11 | OBJ += ../../src/conf.o 12 | PROGS=test 13 | 14 | .PHONY: all run clean 15 | all: $(patsubst %, %.o, $(PROGS)) $(OBJ) $(PROGS) 16 | 17 | %: %.o $(OBJ) 18 | gcc $(CFLAGS) -o $@ $< $(OBJ) $(LDFLAGS) 19 | 20 | clean: 21 | rm -f *.o $(OBJ) $(PROGS) 22 | 23 | run: 24 | ./test >output 25 | -------------------------------------------------------------------------------- /t/test07/README: -------------------------------------------------------------------------------- 1 | Config file check with missing config file 2 | -------------------------------------------------------------------------------- /t/test07/output.ok: -------------------------------------------------------------------------------- 1 | Can't open configuration from data/test.ini: No such file or directory 2 | -------------------------------------------------------------------------------- /t/test07/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "conf.h" 6 | 7 | int main (int argc, char *argv[]) { 8 | conf_set_config_file("data/test.ini"); 9 | 10 | if (conf_load()) 11 | return 0; 12 | 13 | conf_dump(); 14 | conf_unload(); 15 | return 0; 16 | } 17 | 18 | -------------------------------------------------------------------------------- /t/test08/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -Wall 3 | CFLAGS += -I../../libamievent 4 | CFLAGS += -I../../libamievent/libc-jss 5 | CFLAGS += -I../../src/pdu 6 | CFLAGS += -I../../src 7 | CFLAGS += -ggdb 8 | LDFLAGS = -lev -lm 9 | 10 | OBJ += ../../libamievent/libc-jss/ini/ini.o 11 | OBJ += ../../src/conf.o 12 | PROGS=test 13 | 14 | .PHONY: all run clean 15 | all: $(patsubst %, %.o, $(PROGS)) $(OBJ) $(PROGS) 16 | 17 | %: %.o $(OBJ) 18 | gcc $(CFLAGS) -o $@ $< $(OBJ) $(LDFLAGS) 19 | 20 | clean: 21 | rm -f *.o $(OBJ) $(PROGS) 22 | 23 | run: 24 | ./test >output 25 | -------------------------------------------------------------------------------- /t/test08/README: -------------------------------------------------------------------------------- 1 | using configuration variables with conf_device() and conf_root() 2 | -------------------------------------------------------------------------------- /t/test08/data/test.ini: -------------------------------------------------------------------------------- 1 | default = device0 2 | 3 | [device0] 4 | host = localhost ; hostname or IP address where Asterisk is running 5 | port = 5038 ; port where AMI is listening, see asterisk/manager.conf 6 | username = amiuser ; name of Asterisk Manager user which have flag 'call' 7 | password = amipassword ; plaintext password for AMI user 8 | dongle = dongle0 ; dongle name in asterisk/chan_dongle.conf 9 | 10 | [another-mobile-provider] 11 | host = 12 | port = 5038 13 | username = asterisk_ami_user 14 | password = asterisk_ami_password 15 | dongle = dongle0 16 | 17 | -------------------------------------------------------------------------------- /t/test08/output.ok: -------------------------------------------------------------------------------- 1 | Loading config ... 2 | default device name = device0 3 | default's port = 5038 4 | default's username = amiuser 5 | port of non-existent device = 6 | host of non-existent device = 7 | Done 8 | -------------------------------------------------------------------------------- /t/test08/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "conf.h" 6 | 7 | int main (int argc, char *argv[]) { 8 | conf_set_config_file("data/test.ini"); 9 | 10 | printf("Loading config ...\n"); 11 | if (conf_load()) 12 | return 0; 13 | 14 | printf("default device name = %s\n", conf_root()->default_device_name); 15 | printf("default's port = %s\n", conf_device(conf_root()->default_device_name)->port); 16 | printf("default's username = %s\n", conf_device(conf_root()->default_device_name)->username); 17 | printf("port of non-existent device = %s\n", conf_device("akarmi")->port); 18 | printf("host of non-existent device = %s\n", conf_device("akarmi")->host); 19 | 20 | printf("Done\n"); 21 | 22 | return 0; 23 | } 24 | 25 | -------------------------------------------------------------------------------- /t/test09/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -Wall 3 | CFLAGS += -I../../libamievent 4 | CFLAGS += -I../../libamievent/libc-jss 5 | CFLAGS += -I../../src 6 | CFLAGS += -ggdb 7 | LDFLAGS = -lev -lm 8 | 9 | OBJ += ../../src/option.o ../../libamievent/libc-jss/misc.o ../../src/verbose.o 10 | PROGS=test 11 | 12 | CFLAGS += -DVERSION=\"test\" 13 | 14 | .PHONY: all run clean 15 | all: $(patsubst %, %.o, $(PROGS)) $(OBJ) $(PROGS) 16 | 17 | %: %.o $(OBJ) 18 | gcc $(CFLAGS) -o $@ $< $(OBJ) $(LDFLAGS) 19 | 20 | clean: 21 | rm -f *.o $(OBJ) $(PROGS) 22 | 23 | run: 24 | ./test --config=conf -f 59 61 >output 25 | -------------------------------------------------------------------------------- /t/test09/README: -------------------------------------------------------------------------------- 1 | Command-line argument parsing 2 | -------------------------------------------------------------------------------- /t/test09/data/test.ini: -------------------------------------------------------------------------------- 1 | default = device0 2 | 3 | [device0] 4 | host = localhost ; hostname or IP address where Asterisk is running 5 | port = 5038 ; port where AMI is listening, see asterisk/manager.conf 6 | username = amiuser ; name of Asterisk Manager user which have flag 'call' 7 | password = amipassword ; plaintext password for AMI user 8 | dongle = dongle0 ; dongle name in asterisk/chan_dongle.conf 9 | 10 | [another-mobile-provider] 11 | host = 12 | port = 5038 13 | username = asterisk_ami_user 14 | password = asterisk_ami_password 15 | dongle = dongle0 16 | 17 | -------------------------------------------------------------------------------- /t/test09/output.ok: -------------------------------------------------------------------------------- 1 | Before parsing: 2 | option.flash = 0 3 | option.help = 0 4 | option.stdin = 0 5 | option.config = 6 | option.message_text = 7 | option.phone_number = 8 | 9 | Before parsing: 10 | option.flash = 1 11 | option.help = 0 12 | option.stdin = 0 13 | option.config = conf 14 | option.message_text = 61 15 | option.phone_number = 59 16 | 17 | -------------------------------------------------------------------------------- /t/test09/test.c: -------------------------------------------------------------------------------- 1 | /* test.c 2 | * Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu 3 | * 4 | * This program is free software, distributed under the terms of 5 | * the GNU General Public License Version 2. See the LICENSE file 6 | * at the top of the source tree. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include "option.h" 14 | #include "debug.h" 15 | 16 | void dump () { 17 | debi(option.flash); 18 | debi(option.help); 19 | debi(option.stdin); 20 | debs(option.config); 21 | debs(option.message_text); 22 | debs(option.phone_number); 23 | } 24 | 25 | int main (int argc, char *argv[]) { 26 | printf("Before parsing:\n"); 27 | dump(); 28 | printf("\n"); 29 | 30 | option_parse_args(argc, argv); 31 | 32 | printf("Before parsing:\n"); 33 | dump(); 34 | printf("\n"); 35 | 36 | return 0; 37 | } 38 | 39 | -------------------------------------------------------------------------------- /t/test10/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -Wall 3 | CFLAGS += -I../../libamievent 4 | CFLAGS += -I../../libamievent/libc-jss 5 | CFLAGS += -I../../src 6 | CFLAGS += -ggdb 7 | LDFLAGS = -lev -lm 8 | 9 | OBJ += ../../src/option.o ../../libamievent/libc-jss/misc.o ../../src/verbose.o 10 | PROGS=test 11 | 12 | CFLAGS += -DVERSION=\"test\" 13 | 14 | .PHONY: all run clean 15 | all: $(patsubst %, %.o, $(PROGS)) $(OBJ) $(PROGS) 16 | 17 | %: %.o $(OBJ) 18 | gcc $(CFLAGS) -o $@ $< $(OBJ) $(LDFLAGS) 19 | 20 | clean: 21 | rm -f *.o $(OBJ) $(PROGS) 22 | 23 | run: 24 | ./test --config=conf -f 12345 one two three four five >output 25 | -------------------------------------------------------------------------------- /t/test10/README: -------------------------------------------------------------------------------- 1 | Multiple command-line argument to message_text 2 | -------------------------------------------------------------------------------- /t/test10/data/test.ini: -------------------------------------------------------------------------------- 1 | default = device0 2 | 3 | [device0] 4 | host = localhost ; hostname or IP address where Asterisk is running 5 | port = 5038 ; port where AMI is listening, see asterisk/manager.conf 6 | username = amiuser ; name of Asterisk Manager user which have flag 'call' 7 | password = amipassword ; plaintext password for AMI user 8 | dongle = dongle0 ; dongle name in asterisk/chan_dongle.conf 9 | 10 | [another-mobile-provider] 11 | host = 12 | port = 5038 13 | username = asterisk_ami_user 14 | password = asterisk_ami_password 15 | dongle = dongle0 16 | 17 | -------------------------------------------------------------------------------- /t/test10/output.ok: -------------------------------------------------------------------------------- 1 | After parsing: 2 | option.flash = 1 3 | option.help = 0 4 | option.stdin = 0 5 | option.config = conf 6 | option.message_text = one two three four five 7 | option.phone_number = 12345 8 | 9 | -------------------------------------------------------------------------------- /t/test10/test.c: -------------------------------------------------------------------------------- 1 | /* test.c 2 | * Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu 3 | * 4 | * This program is free software, distributed under the terms of 5 | * the GNU General Public License Version 2. See the LICENSE file 6 | * at the top of the source tree. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include "option.h" 14 | #include "debug.h" 15 | 16 | void dump () { 17 | debi(option.flash); 18 | debi(option.help); 19 | debi(option.stdin); 20 | debs(option.config); 21 | debs(option.message_text); 22 | debs(option.phone_number); 23 | } 24 | 25 | int main (int argc, char *argv[]) { 26 | option_parse_args(argc, argv); 27 | 28 | printf("After parsing:\n"); 29 | dump(); 30 | printf("\n"); 31 | 32 | return 0; 33 | } 34 | 35 | -------------------------------------------------------------------------------- /t/test11/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -Wall 3 | CFLAGS += -I../../libamievent/libc-jss 4 | CFLAGS += -I../../src 5 | CFLAGS += -ggdb 6 | LDFLAGS = -lev -lm 7 | 8 | OBJ += ../../libamievent/libc-jss/misc.o 9 | OBJ += ../../src/pnv.o 10 | PROGS=test 11 | 12 | CFLAGS += -DVERSION=\"test\" 13 | 14 | .PHONY: all run clean 15 | all: $(patsubst %, %.o, $(PROGS)) $(OBJ) $(PROGS) 16 | 17 | %: %.o $(OBJ) 18 | gcc $(CFLAGS) -o $@ $< $(OBJ) $(LDFLAGS) 19 | 20 | clean: 21 | rm -f *.o $(OBJ) $(PROGS) 22 | 23 | run: 24 | ./test >output 25 | -------------------------------------------------------------------------------- /t/test11/README: -------------------------------------------------------------------------------- 1 | phone number validation 2 | -------------------------------------------------------------------------------- /t/test11/output.ok: -------------------------------------------------------------------------------- 1 | STATE NUM-ORIG NUM-CONVERTED COUNTRY FAIL MSG OK MSG 2 | ---------------------------------------------------------------------------------------- 3 | PNV_OK 06201234567 +36201234567 Hungary (null) Telenor 4 | PNV_OK 06301234567 +36301234567 Hungary (null) T-Mobile 5 | PNV_OK 06701234567 +36701234567 Hungary (null) Vodafone 6 | PNV_OK 06311234567 +36311234567 Hungary (null) Tesco mobile 7 | PNV_FAIL 0670123456 +3670123456 Hungary kevés vagy sok számjegy (null) 8 | PNV_FAIL 067012345678 +367012345678 Hungary kevés vagy sok számjegy (null) 9 | PNV_FAIL 0613095200 +3613095200 Hungary nem mobil körzet (null) 10 | PNV_FAIL 1717 1717 Hungary emeltdíjas (null) 11 | PNV_OK 1270 1270 Hungary (null) SMS szolgálatás 12 | PNV_FAIL 0680123456 +3680123456 Hungary zöld szám (null) 13 | PNV_FAIL 0690123456 +3690123456 Hungary emeltdíjas (null) 14 | PNV_UNKNOWN 06701234567 06701234567 (null) helyi számot nem lehet vizsgálni ismeretlen régióban (null) 15 | PNV_FAIL +3620123456 +3620123456 Hungary kevés vagy sok számjegy (null) 16 | PNV_OK +36201234567 +36201234567 Hungary (null) Telenor 17 | PNV_FAIL +06201234567 +06201234567 (null) rossz nemzetközi formátum (null) 18 | PNV_UNKNOWN +9999999999 +9999999999 (null) országkód nem található (null) 19 | PNV_FAIL 0620 +3620 Hungary kevés számjegy (null) 20 | PNV_FAIL 062 +362 Hungary kevés számjegy (null) 21 | PNV_FAIL 06 +36 Hungary kevés számjegy (null) 22 | PNV_FAIL + + (null) kevés számjegy (null) 23 | PNV_FAIL +362012+4567 +362012+4567 (null) túl sok + karakter (null) 24 | PNV_OK 36201234567 +36201234567 Hungary (null) Telenor 25 | PNV_OK 201234567 +36201234567 Hungary (null) Telenor 26 | PNV_OK 301234567 +36301234567 Hungary (null) T-Mobile 27 | PNV_OK 311234567 +36311234567 Hungary (null) Tesco mobile 28 | PNV_OK 701234567 +36701234567 Hungary (null) Vodafone 29 | PNV_OK 20 / 123 - 4567 +36201234567 Hungary (null) Telenor 30 | -------------------------------------------------------------------------------- /t/test11/test.c: -------------------------------------------------------------------------------- 1 | /* test.c 2 | * Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu 3 | * 4 | * This program is free software, distributed under the terms of 5 | * the GNU General Public License Version 2. See the LICENSE file 6 | * at the top of the source tree. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "pnv.h" 15 | 16 | #undef fmt 17 | #define fmt "%-11s %-14s %-15s %-20s %-10s %-10s\n" 18 | 19 | void p (const char *locale, const char *phone_number) { 20 | pnv_t *pnv; 21 | pnv = pnv_new(phone_number); 22 | pnv_set_locale(pnv, locale); 23 | 24 | enum pnv_state state = pnv_validate(pnv); 25 | printf(fmt 26 | , pnv_state_str(state) 27 | , pnv_get_phone_number_original(pnv) 28 | , pnv_get_phone_number_converted(pnv) 29 | , pnv_get_country(pnv) 30 | , pnv_get_msg_fail(pnv) 31 | , pnv_get_msg_ok(pnv) 32 | ); 33 | pnv_destroy(pnv); 34 | } 35 | 36 | int main (int argc, const char *argv[]) { 37 | printf(fmt, "STATE", "NUM-ORIG", "NUM-CONVERTED", "COUNTRY", "FAIL MSG", "OK MSG"); 38 | printf("----------------------------------------------------------------------------------------\n"); 39 | p("hu", "06201234567"); 40 | p("hu", "06301234567"); 41 | p("hu", "06701234567"); 42 | p("hu", "06311234567"); 43 | p("hu", "0670123456"); 44 | p("hu", "067012345678"); 45 | p("hu", "0613095200"); 46 | p("hu", "1717"); 47 | p("hu", "1270"); 48 | p("hu", "0680123456"); 49 | p("hu", "0690123456"); 50 | p("", "06701234567"); 51 | p("", "+3620123456"); 52 | p("", "+36201234567"); 53 | p("", "+06201234567"); 54 | p("", "+9999999999"); 55 | p("hu", "0620"); 56 | p("hu", "062"); 57 | p("hu", "06"); 58 | p("", "+"); 59 | p("hu", "+362012+4567"); 60 | p("hu", "36201234567"); 61 | p("hu", "201234567"); 62 | p("hu", "301234567"); 63 | p("hu", "311234567"); 64 | p("hu", "701234567"); 65 | p("hu", "20 / 123 - 4567"); 66 | 67 | return 0; 68 | } 69 | 70 | -------------------------------------------------------------------------------- /t/test12/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -Wall 3 | CFLAGS += -I../../libamievent/libc-jss 4 | CFLAGS += -I../../src 5 | CFLAGS += -ggdb 6 | LDFLAGS = -lev -lm 7 | 8 | OBJ += ../../libamievent/libc-jss/misc.o 9 | OBJ += ../../src/pnv.o 10 | PROGS=test 11 | 12 | CFLAGS += -DVERSION=\"test\" 13 | 14 | .PHONY: all run clean 15 | all: $(patsubst %, %.o, $(PROGS)) $(OBJ) $(PROGS) 16 | 17 | %: %.o $(OBJ) 18 | gcc $(CFLAGS) -o $@ $< $(OBJ) $(LDFLAGS) 19 | 20 | clean: 21 | rm -f *.o $(OBJ) $(PROGS) 22 | 23 | run: 24 | ./test >output 25 | -------------------------------------------------------------------------------- /t/test12/README: -------------------------------------------------------------------------------- 1 | pnv_json 2 | -------------------------------------------------------------------------------- /t/test12/output.ok: -------------------------------------------------------------------------------- 1 | {"state": "PNV_OK", "provider": "T-Mobile", "invalidMsg": "", "revisedNumber": "+36301234567"} 2 | {"state": "PNV_FAIL", "provider": "", "invalidMsg": "kevés vagy sok számjegy", "revisedNumber": ""} 3 | {"state": "PNV_UNKNOWN", "provider": "", "invalidMsg": "helyi számot nem lehet vizsgálni ismeretlen régióban", "revisedNumber": ""} 4 | {"state": "PNV_OK", "provider": "Vodafone", "invalidMsg": "", "revisedNumber": "+36701234567"} 5 | -------------------------------------------------------------------------------- /t/test12/test.c: -------------------------------------------------------------------------------- 1 | /* test.c 2 | * Copyright © 2014, Andras Jeszenszky, JSS & Hayer IT - http://www.jsshayer.hu 3 | * 4 | * This program is free software, distributed under the terms of 5 | * the GNU General Public License Version 2. See the LICENSE file 6 | * at the top of the source tree. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | int main (int argc, const char *argv[]) { 15 | system("../../src/pnv_json 06301234567 hu"); 16 | system("../../src/pnv_json 0630123456 hu"); 17 | system("../../src/pnv_json 0630123456"); 18 | system("../../src/pnv_json +36-70.123.4567"); 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /update_git_submodules: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | git submodule update --recursive --init 4 | --------------------------------------------------------------------------------