From 979475b3124b0433ed49cd12021e0644e949dc1d Mon Sep 17 00:00:00 2001 From: Ibnu Maksum Date: Sat, 11 Mar 2017 02:51:06 +0700 Subject: [PATCH] PHPMixBill v5.0 - First Upload --- .DS_Store | Bin 0 -> 6148 bytes .gitignore | 1 + LICENSE | 339 + README.md | 59 + admin/index.php | 11 + index.php | 12 + system/.DS_Store | Bin 0 -> 6148 bytes system/autoload/Admin.php | 17 + system/autoload/App.php | 16 + system/autoload/PEAR2/Autoload.php | 243 + system/autoload/PEAR2/Cache/SHM.php | 371 + .../autoload/PEAR2/Cache/SHM/Adapter/APC.php | 406 + .../PEAR2/Cache/SHM/Adapter/Placebo.php | 358 + .../PEAR2/Cache/SHM/Adapter/Wincache.php | 383 + system/autoload/PEAR2/Cache/SHM/Exception.php | 34 + .../Cache/SHM/InvalidArgumentException.php | 35 + system/autoload/PEAR2/Console/Color.php | 369 + .../PEAR2/Console/Color/Backgrounds.php | 136 + .../PEAR2/Console/Color/Exception.php | 28 + system/autoload/PEAR2/Console/Color/Flags.php | 88 + system/autoload/PEAR2/Console/Color/Fonts.php | 136 + .../autoload/PEAR2/Console/Color/Styles.php | 130 + .../Color/UnexpectedValueException.php | 40 + system/autoload/PEAR2/Console/CommandLine.php | 1289 + .../PEAR2/Console/CommandLine/Action.php | 142 + .../Console/CommandLine/Action/Callback.php | 86 + .../Console/CommandLine/Action/Counter.php | 84 + .../PEAR2/Console/CommandLine/Action/Help.php | 58 + .../PEAR2/Console/CommandLine/Action/List.php | 69 + .../Console/CommandLine/Action/Password.php | 90 + .../Console/CommandLine/Action/StoreArray.php | 76 + .../Console/CommandLine/Action/StoreFalse.php | 62 + .../Console/CommandLine/Action/StoreFloat.php | 73 + .../Console/CommandLine/Action/StoreInt.php | 73 + .../CommandLine/Action/StoreString.php | 60 + .../Console/CommandLine/Action/StoreTrue.php | 61 + .../Console/CommandLine/Action/Version.php | 58 + .../PEAR2/Console/CommandLine/Argument.php | 94 + .../PEAR2/Console/CommandLine/Command.php | 72 + .../CommandLine/CustomMessageProvider.php | 67 + .../PEAR2/Console/CommandLine/Element.php | 151 + .../PEAR2/Console/CommandLine/Exception.php | 90 + .../Console/CommandLine/MessageProvider.php | 57 + .../CommandLine/MessageProvider/Default.php | 143 + .../PEAR2/Console/CommandLine/Option.php | 393 + .../PEAR2/Console/CommandLine/Outputter.php | 64 + .../Console/CommandLine/Outputter/Default.php | 78 + .../PEAR2/Console/CommandLine/Renderer.php | 72 + .../Console/CommandLine/Renderer/Default.php | 441 + .../PEAR2/Console/CommandLine/Result.php | 72 + .../PEAR2/Console/CommandLine/XmlParser.php | 303 + system/autoload/PEAR2/Net/RouterOS/Client.php | 819 + .../PEAR2/Net/RouterOS/Communicator.php | 671 + .../PEAR2/Net/RouterOS/DataFlowException.php | 44 + .../autoload/PEAR2/Net/RouterOS/Exception.php | 34 + .../Net/RouterOS/InvalidArgumentException.php | 43 + .../PEAR2/Net/RouterOS/LengthException.php | 93 + .../autoload/PEAR2/Net/RouterOS/Message.php | 237 + .../Net/RouterOS/NotSupportedException.php | 91 + system/autoload/PEAR2/Net/RouterOS/Query.php | 267 + .../autoload/PEAR2/Net/RouterOS/Registry.php | 279 + .../autoload/PEAR2/Net/RouterOS/Request.php | 403 + .../autoload/PEAR2/Net/RouterOS/Response.php | 335 + .../PEAR2/Net/RouterOS/ResponseCollection.php | 569 + .../PEAR2/Net/RouterOS/SocketException.php | 44 + .../Net/RouterOS/UnexpectedValueException.php | 88 + system/autoload/PEAR2/Net/RouterOS/Util.php | 1177 + .../PEAR2/Net/Transmitter/Exception.php | 36 + .../Net/Transmitter/FilterCollection.php | 229 + .../PEAR2/Net/Transmitter/LockException.php | 36 + .../PEAR2/Net/Transmitter/NetworkStream.php | 181 + .../PEAR2/Net/Transmitter/SocketException.php | 124 + .../autoload/PEAR2/Net/Transmitter/Stream.php | 605 + .../PEAR2/Net/Transmitter/StreamException.php | 119 + .../PEAR2/Net/Transmitter/TcpClient.php | 400 + .../Net/Transmitter/TcpServerConnection.php | 147 + system/autoload/Paginator.php | 105 + system/autoload/Password.php | 34 + system/autoload/Router.php | 16 + system/autoload/Timezone.php | 44 + system/autoload/User.php | 22 + system/autoload/Validator.php | 284 + system/autoload/index.html | 8 + system/boot.php | 196 + system/config.sample.php | 9 + system/controllers/accounts.php | 183 + system/controllers/admin.php | 54 + system/controllers/autoload.php | 45 + system/controllers/bandwidth.php | 141 + system/controllers/customers.php | 297 + system/controllers/dashboard.php | 59 + system/controllers/default.php | 11 + system/controllers/export.php | 355 + system/controllers/home.php | 20 + system/controllers/index.html | 8 + system/controllers/login.php | 55 + system/controllers/logout.php | 12 + system/controllers/message.php | 21 + system/controllers/order.php | 20 + system/controllers/pm.php | 21 + system/controllers/pool.php | 185 + system/controllers/prepaid.php | 702 + system/controllers/reports.php | 88 + system/controllers/routers.php | 148 + system/controllers/services.php | 455 + system/controllers/settings.php | 478 + system/controllers/voucher.php | 258 + system/cron.php | 109 + system/index.html | 8 + system/install/.DS_Store | Bin 0 -> 6148 bytes system/install/css/bootstrap.css | 6203 +++ system/install/css/bootstrap.css.map | 1 + system/install/css/bootstrap.min.css | 5 + system/install/css/style.css | 73 + system/install/img/favicon.png | Bin 0 -> 6761 bytes system/install/img/logo.png | Bin 0 -> 8272 bytes system/install/index.php | 61 + system/install/phpmixbill.sql | 239 + system/install/step2.php | 71 + system/install/step3.php | 72 + system/install/step4.php | 108 + system/install/step5.php | 46 + system/lan/.DS_Store | Bin 0 -> 6148 bytes system/lan/english/common.lan.php | 229 + system/lan/english/index.html | 8 + system/lan/index.html | 8 + system/lan/indonesia/common.lan.php | 226 + system/lan/indonesia/index.html | 8 + system/orm.php | 2457 ++ system/uploads/.DS_Store | Bin 0 -> 6148 bytes system/uploads/_sysfrm_tmp_/index.html | 8 + system/uploads/admin.png | Bin 0 -> 1914 bytes system/uploads/index.html | 8 + system/uploads/logo.png | Bin 0 -> 1876 bytes system/uploads/system/index.html | 8 + system/uploads/user.jpg | Bin 0 -> 12058 bytes system/vendors/.DS_Store | Bin 0 -> 6148 bytes system/vendors/index.html | 8 + system/vendors/mpdf/CHANGELOG.txt | 3110 ++ system/vendors/mpdf/CREDITS.txt | 92 + system/vendors/mpdf/LICENSE.txt | 340 + system/vendors/mpdf/README.txt | 130 + system/vendors/mpdf/classes/barcode.php | 1972 + system/vendors/mpdf/classes/bmp.php | 248 + system/vendors/mpdf/classes/cssmgr.php | 1721 + system/vendors/mpdf/classes/desktop.ini | 4 + system/vendors/mpdf/classes/directw.php | 412 + system/vendors/mpdf/classes/gif.php | 700 + system/vendors/mpdf/classes/grad.php | 724 + system/vendors/mpdf/classes/indic.php | 1714 + system/vendors/mpdf/classes/meter.php | 282 + system/vendors/mpdf/classes/mpdfform.php | 1550 + system/vendors/mpdf/classes/myanmar.php | 481 + system/vendors/mpdf/classes/otl.php | 5719 +++ system/vendors/mpdf/classes/otl_dump.php | 3897 ++ system/vendors/mpdf/classes/sea.php | 349 + system/vendors/mpdf/classes/svg.php | 3441 ++ system/vendors/mpdf/classes/tocontents.php | 509 + system/vendors/mpdf/classes/ttfontsuni.php | 4732 +++ .../mpdf/classes/ttfontsuni_analysis.php | 408 + system/vendors/mpdf/classes/ucdn.php | 2920 ++ system/vendors/mpdf/classes/wmf.php | 236 + .../collations/Afrikaans_South_Africa.php | 105 + .../mpdf/collations/Albanian_Albania.php | 111 + .../mpdf/collations/Alsatian_France.php | 105 + .../mpdf/collations/Arabic_Algeria.php | 68 + .../mpdf/collations/Arabic_Bahrain.php | 68 + .../vendors/mpdf/collations/Arabic_Egypt.php | 68 + .../vendors/mpdf/collations/Arabic_Iraq.php | 68 + .../vendors/mpdf/collations/Arabic_Jordan.php | 68 + .../vendors/mpdf/collations/Arabic_Kuwait.php | 68 + .../mpdf/collations/Arabic_Lebanon.php | 68 + .../vendors/mpdf/collations/Arabic_Libya.php | 68 + .../mpdf/collations/Arabic_Morocco.php | 68 + .../vendors/mpdf/collations/Arabic_Oman.php | 68 + .../mpdf/collations/Arabic_Pseudo_RTL.php | 68 + .../vendors/mpdf/collations/Arabic_Qatar.php | 68 + .../mpdf/collations/Arabic_Saudi_Arabia.php | 68 + .../vendors/mpdf/collations/Arabic_Syria.php | 68 + .../mpdf/collations/Arabic_Tunisia.php | 68 + .../vendors/mpdf/collations/Arabic_Yemen.php | 68 + .../Azeri_(Cyrillic)_Azerbaijan.php | 80 + .../collations/Azeri_(Latin)_Azerbaijan.php | 98 + .../mpdf/collations/Bashkir_Russia.php | 80 + .../vendors/mpdf/collations/Basque_Spain.php | 105 + .../mpdf/collations/Belarusian_Belarus.php | 80 + ...nian_(Cyrillic)_Bosnia_and_Herzegovina.php | 80 + ...Bosnian_(Latin)_Bosnia_and_Herzegovina.php | 107 + .../vendors/mpdf/collations/Breton_France.php | 105 + .../mpdf/collations/Bulgarian_Bulgaria.php | 80 + .../vendors/mpdf/collations/Catalan_Spain.php | 105 + .../mpdf/collations/Corsican_France.php | 105 + ...roatian_(Latin)_Bosnia_and_Herzegovina.php | 107 + .../mpdf/collations/Croatian_Croatia.php | 107 + .../mpdf/collations/Czech_Czech_Republic.php | 108 + .../mpdf/collations/Danish_Denmark.php | 102 + .../mpdf/collations/Dari_Afghanistan.php | 65 + .../vendors/mpdf/collations/Dutch_Belgium.php | 105 + .../mpdf/collations/Dutch_Netherlands.php | 105 + .../mpdf/collations/English_Australia.php | 105 + .../mpdf/collations/English_Belize.php | 105 + .../mpdf/collations/English_Canada.php | 105 + .../mpdf/collations/English_Caribbean.php | 105 + .../vendors/mpdf/collations/English_India.php | 105 + .../mpdf/collations/English_Ireland.php | 105 + .../mpdf/collations/English_Jamaica.php | 105 + .../mpdf/collations/English_Malaysia.php | 105 + .../mpdf/collations/English_New_Zealand.php | 105 + .../English_Republic_of_the_Philippines.php | 105 + .../mpdf/collations/English_Singapore.php | 105 + .../mpdf/collations/English_South_Africa.php | 105 + .../English_Trinidad_and_Tobago.php | 105 + .../collations/English_United_Kingdom.php | 104 + .../mpdf/collations/English_United_States.php | 104 + .../mpdf/collations/English_Zimbabwe.php | 105 + .../mpdf/collations/Estonian_Estonia.php | 94 + .../mpdf/collations/Faroese_Faroe_Islands.php | 105 + .../mpdf/collations/Filipino_Philippines.php | 105 + .../mpdf/collations/Finnish_Finland.php | 103 + .../mpdf/collations/French_Belgium.php | 105 + .../vendors/mpdf/collations/French_Canada.php | 105 + .../vendors/mpdf/collations/French_France.php | 105 + .../mpdf/collations/French_Luxembourg.php | 105 + .../French_Principality_of_Monaco.php | 105 + .../mpdf/collations/French_Switzerland.php | 105 + .../mpdf/collations/Frisian_Netherlands.php | 105 + .../mpdf/collations/Galician_Spain.php | 105 + .../mpdf/collations/German_Austria.php | 105 + .../mpdf/collations/German_Germany.php | 105 + .../mpdf/collations/German_Liechtenstein.php | 105 + .../mpdf/collations/German_Luxembourg.php | 105 + .../mpdf/collations/German_Switzerland.php | 105 + .../vendors/mpdf/collations/Greek_Greece.php | 75 + .../mpdf/collations/Greenlandic_Greenland.php | 102 + .../mpdf/collations/Hausa_(Latin)_Nigeria.php | 105 + .../vendors/mpdf/collations/Hebrew_Israel.php | 60 + .../mpdf/collations/Hungarian_Hungary.php | 71 + .../mpdf/collations/Icelandic_Iceland.php | 95 + .../vendors/mpdf/collations/Igbo_Nigeria.php | 105 + .../mpdf/collations/Indonesian_Indonesia.php | 105 + .../collations/Inuktitut_(Latin)_Canada.php | 105 + .../Invariant_Language_Invariant_Country.php | 105 + .../vendors/mpdf/collations/Irish_Ireland.php | 105 + .../vendors/mpdf/collations/Italian_Italy.php | 105 + .../mpdf/collations/Italian_Switzerland.php | 105 + .../mpdf/collations/Kinyarwanda_Rwanda.php | 105 + .../mpdf/collations/Kiswahili_Kenya.php | 105 + .../mpdf/collations/Kyrgyz_Kyrgyzstan.php | 80 + .../mpdf/collations/Latvian_Latvia.php | 88 + .../mpdf/collations/Lithuanian_Lithuania.php | 92 + .../mpdf/collations/Lower_Sorbian_Germany.php | 105 + .../collations/Luxembourgish_Luxembourg.php | 105 + .../Macedonian_(FYROM)_Macedonia_(FYROM).php | 79 + .../collations/Malay_Brunei_Darussalam.php | 105 + .../mpdf/collations/Malay_Malaysia.php | 105 + .../mpdf/collations/Mapudungun_Chile.php | 103 + .../vendors/mpdf/collations/Mohawk_Canada.php | 105 + .../Mongolian_(Cyrillic)_Mongolia.php | 80 + .../collations/Norwegian_(Nynorsk)_Norway.php | 102 + .../mpdf/collations/Occitan_France.php | 105 + .../vendors/mpdf/collations/Persian_Iran.php | 66 + .../vendors/mpdf/collations/Polish_Poland.php | 103 + .../mpdf/collations/Portuguese_Brazil.php | 105 + .../mpdf/collations/Portuguese_Portugal.php | 105 + .../mpdf/collations/Quechua_Bolivia.php | 105 + .../mpdf/collations/Quechua_Ecuador.php | 105 + .../vendors/mpdf/collations/Quechua_Peru.php | 105 + .../mpdf/collations/Romanian_Romania.php | 108 + .../mpdf/collations/Romansh_Switzerland.php | 87 + .../mpdf/collations/Russian_Russia.php | 80 + .../mpdf/collations/Sami_(Inari)_Finland.php | 95 + .../mpdf/collations/Sami_(Lule)_Norway.php | 95 + .../mpdf/collations/Sami_(Lule)_Sweden.php | 95 + .../collations/Sami_(Northern)_Finland.php | 95 + .../collations/Sami_(Northern)_Norway.php | 95 + .../collations/Sami_(Northern)_Sweden.php | 95 + .../mpdf/collations/Sami_(Skolt)_Finland.php | 95 + .../collations/Sami_(Southern)_Norway.php | 95 + .../collations/Sami_(Southern)_Sweden.php | 95 + ...bian_(Cyrillic)_Bosnia_and_Herzegovina.php | 80 + .../collations/Serbian_(Cyrillic)_Serbia.php | 80 + ...Serbian_(Latin)_Bosnia_and_Herzegovina.php | 107 + .../collations/Serbian_(Latin)_Serbia.php | 107 + .../Sesotho_sa_Leboa_South_Africa.php | 105 + .../mpdf/collations/Setswana_South_Africa.php | 105 + .../mpdf/collations/Slovak_Slovakia.php | 106 + .../mpdf/collations/Slovenian_Slovenia.php | 105 + .../mpdf/collations/Spanish_Argentina.php | 104 + .../mpdf/collations/Spanish_Bolivia.php | 104 + .../vendors/mpdf/collations/Spanish_Chile.php | 104 + .../mpdf/collations/Spanish_Colombia.php | 104 + .../mpdf/collations/Spanish_Costa_Rica.php | 104 + .../collations/Spanish_Dominican_Republic.php | 104 + .../mpdf/collations/Spanish_Ecuador.php | 104 + .../mpdf/collations/Spanish_El_Salvador.php | 104 + .../mpdf/collations/Spanish_Guatemala.php | 104 + .../mpdf/collations/Spanish_Honduras.php | 104 + .../mpdf/collations/Spanish_Mexico.php | 104 + .../mpdf/collations/Spanish_Nicaragua.php | 104 + .../mpdf/collations/Spanish_Panama.php | 104 + .../mpdf/collations/Spanish_Paraguay.php | 104 + .../vendors/mpdf/collations/Spanish_Peru.php | 104 + .../mpdf/collations/Spanish_Puerto_Rico.php | 104 + .../vendors/mpdf/collations/Spanish_Spain.php | 104 + .../mpdf/collations/Spanish_United_States.php | 105 + .../mpdf/collations/Spanish_Uruguay.php | 104 + .../mpdf/collations/Spanish_Venezuela.php | 104 + .../mpdf/collations/Swedish_Finland.php | 103 + .../mpdf/collations/Swedish_Sweden.php | 103 + .../Tajik_(Cyrillic)_Tajikistan.php | 80 + .../collations/Tamazight_(Latin)_Algeria.php | 105 + .../vendors/mpdf/collations/Tatar_Russia.php | 80 + .../mpdf/collations/Turkish_Turkey.php | 98 + .../mpdf/collations/Turkmen_Turkmenistan.php | 105 + .../mpdf/collations/Ukrainian_Ukraine.php | 79 + .../mpdf/collations/Upper_Sorbian_Germany.php | 103 + .../Urdu_Islamic_Republic_of_Pakistan.php | 67 + .../Uzbek_(Cyrillic)_Uzbekistan.php | 80 + .../collations/Uzbek_(Latin)_Uzbekistan.php | 103 + .../mpdf/collations/Vietnamese_Vietnam.php | 91 + .../mpdf/collations/Welsh_United_Kingdom.php | 105 + .../vendors/mpdf/collations/Wolof_Senegal.php | 105 + .../vendors/mpdf/collations/Yakut_Russia.php | 79 + .../mpdf/collations/Yoruba_Nigeria.php | 105 + .../mpdf/collations/isiXhosa_South_Africa.php | 105 + .../mpdf/collations/isiZulu_South_Africa.php | 105 + system/vendors/mpdf/compress.php | 171 + system/vendors/mpdf/config.php | 620 + .../mpdf/config_fonts-distr-without-OTL.php | 251 + system/vendors/mpdf/config_fonts.php | 341 + system/vendors/mpdf/config_lang2fonts.php | 501 + system/vendors/mpdf/config_script2lang.php | 158 + system/vendors/mpdf/font/ccourier.php | 10 + system/vendors/mpdf/font/ccourierb.php | 10 + system/vendors/mpdf/font/ccourierbi.php | 10 + system/vendors/mpdf/font/ccourieri.php | 10 + system/vendors/mpdf/font/chelvetica.php | 21 + system/vendors/mpdf/font/chelveticab.php | 22 + system/vendors/mpdf/font/chelveticabi.php | 22 + system/vendors/mpdf/font/chelveticai.php | 22 + system/vendors/mpdf/font/csymbol.php | 22 + system/vendors/mpdf/font/ctimes.php | 22 + system/vendors/mpdf/font/ctimesb.php | 22 + system/vendors/mpdf/font/ctimesbi.php | 22 + system/vendors/mpdf/font/ctimesi.php | 22 + system/vendors/mpdf/font/czapfdingbats.php | 22 + system/vendors/mpdf/graph.php | 720 + system/vendors/mpdf/graph_cache/dummy.txt | 2 + system/vendors/mpdf/includes/CJKdata.php | 101 + system/vendors/mpdf/includes/functions.php | 172 + system/vendors/mpdf/includes/linebrdictK.dat | Bin 0 -> 1053144 bytes system/vendors/mpdf/includes/linebrdictL.dat | Bin 0 -> 126932 bytes system/vendors/mpdf/includes/linebrdictT.dat | Bin 0 -> 301751 bytes system/vendors/mpdf/includes/no_image.jpg | Bin 0 -> 7888 bytes system/vendors/mpdf/includes/out.php | 59 + system/vendors/mpdf/includes/subs_core.php | 454 + .../vendors/mpdf/includes/subs_win-1252.php | 308 + system/vendors/mpdf/includes/upperCase.php | 975 + system/vendors/mpdf/lang2fonts.css | 394 + system/vendors/mpdf/mpdf.css | 83 + system/vendors/mpdf/mpdf.php | 32752 ++++++++++++++++ .../mpdf/mpdfi/filters/FilterASCII85.php | 98 + .../vendors/mpdf/mpdfi/filters/FilterLZW.php | 154 + system/vendors/mpdf/mpdfi/fpdi_pdf_parser.php | 363 + system/vendors/mpdf/mpdfi/pdf_context.php | 78 + system/vendors/mpdf/mpdfi/pdf_parser.php | 690 + system/vendors/mpdf/patterns/NOTES.txt | 5 + system/vendors/mpdf/patterns/de.php | 9 + system/vendors/mpdf/patterns/dictionary.txt | 1 + system/vendors/mpdf/patterns/en.php | 10 + system/vendors/mpdf/patterns/es.php | 11 + system/vendors/mpdf/patterns/fi.php | 9 + system/vendors/mpdf/patterns/fr.php | 12 + system/vendors/mpdf/patterns/it.php | 11 + system/vendors/mpdf/patterns/nl.php | 11 + system/vendors/mpdf/patterns/pl.php | 9 + system/vendors/mpdf/patterns/ru.php | 12 + system/vendors/mpdf/patterns/sv.php | 12 + system/vendors/mpdf/progbar.css | 66 + system/vendors/mpdf/tmp/dummy.txt | 2 + .../mpdf/ttfontdata/dejavusans.GDEFdata.php | 17 + .../mpdf/ttfontdata/dejavusans.GPOSdata.php | 595 + .../ttfontdata/dejavusans.GSUB.arab.DFLT.php | 130 + .../ttfontdata/dejavusans.GSUB.arab.KUR .php | 130 + .../ttfontdata/dejavusans.GSUB.arab.SND .php | 130 + .../ttfontdata/dejavusans.GSUB.arab.URD .php | 130 + .../ttfontdata/dejavusans.GSUB.nko .DFLT.php | 48 + .../ttfontdata/dejavusans.GSUBGPOStables.dat | Bin 0 -> 45690 bytes .../mpdf/ttfontdata/dejavusans.GSUBdata.php | 959 + .../vendors/mpdf/ttfontdata/dejavusans.cw.dat | Bin 0 -> 131072 bytes .../mpdf/ttfontdata/dejavusans.cw127.php | 163 + .../mpdf/ttfontdata/dejavusans.gid.dat | Bin 0 -> 196608 bytes .../mpdf/ttfontdata/dejavusans.mtx.php | 4950 +++ .../dejavusanscondensed.GDEFdata.php | 17 + .../dejavusanscondensed.GPOSdata.php | 595 + .../dejavusanscondensed.GSUB.arab.DFLT.php | 130 + .../dejavusanscondensed.GSUB.arab.KUR .php | 130 + .../dejavusanscondensed.GSUB.arab.SND .php | 130 + .../dejavusanscondensed.GSUB.arab.URD .php | 130 + .../dejavusanscondensed.GSUB.nko .DFLT.php | 48 + .../dejavusanscondensed.GSUBGPOStables.dat | Bin 0 -> 45690 bytes .../dejavusanscondensed.GSUBdata.php | 959 + .../ttfontdata/dejavusanscondensed.cw.dat | Bin 0 -> 131072 bytes .../ttfontdata/dejavusanscondensed.cw127.php | 163 + .../ttfontdata/dejavusanscondensed.gid.dat | Bin 0 -> 196608 bytes .../ttfontdata/dejavusanscondensed.mtx.php | 4950 +++ .../dejavusanscondensedB.GDEFdata.php | 14 + .../dejavusanscondensedB.GPOSdata.php | 550 + .../dejavusanscondensedB.GSUB.arab.DFLT.php | 130 + .../dejavusanscondensedB.GSUB.arab.KUR .php | 130 + .../dejavusanscondensedB.GSUB.arab.SND .php | 130 + .../dejavusanscondensedB.GSUB.arab.URD .php | 130 + .../dejavusanscondensedB.GSUB.nko .DFLT.php | 48 + .../dejavusanscondensedB.GSUBGPOStables.dat | Bin 0 -> 39864 bytes .../dejavusanscondensedB.GSUBdata.php | 944 + .../ttfontdata/dejavusanscondensedB.cw.dat | Bin 0 -> 131072 bytes .../ttfontdata/dejavusanscondensedB.cw127.php | 163 + .../ttfontdata/dejavusanscondensedB.gid.dat | Bin 0 -> 196608 bytes .../ttfontdata/dejavusanscondensedB.mtx.php | 3669 ++ .../dejavusanscondensedI.GDEFdata.php | 14 + .../dejavusanscondensedI.GPOSdata.php | 433 + .../dejavusanscondensedI.GSUBGPOStables.dat | Bin 0 -> 24176 bytes .../dejavusanscondensedI.GSUBdata.php | 374 + .../ttfontdata/dejavusanscondensedI.cw.dat | Bin 0 -> 131072 bytes .../ttfontdata/dejavusanscondensedI.gid.dat | Bin 0 -> 196608 bytes .../ttfontdata/dejavusanscondensedI.mtx.php | 2801 ++ .../ttfontdata/dejavusansmono.GDEFdata.php | 13 + .../ttfontdata/dejavusansmono.GPOSdata.php | 228 + .../dejavusansmono.GSUB.arab.DFLT.php | 71 + .../dejavusansmono.GSUBGPOStables.dat | Bin 0 -> 15914 bytes .../ttfontdata/dejavusansmono.GSUBdata.php | 206 + .../mpdf/ttfontdata/dejavusansmono.cw.dat | Bin 0 -> 131072 bytes .../mpdf/ttfontdata/dejavusansmono.cw127.php | 107 + .../mpdf/ttfontdata/dejavusansmono.gid.dat | Bin 0 -> 196608 bytes .../mpdf/ttfontdata/dejavusansmono.mtx.php | 601 + .../ttfontdata/dejavusansmonoB.GDEFdata.php | 13 + .../ttfontdata/dejavusansmonoB.GPOSdata.php | 147 + .../dejavusansmonoB.GSUB.arab.DFLT.php | 71 + .../dejavusansmonoB.GSUBGPOStables.dat | Bin 0 -> 8146 bytes .../ttfontdata/dejavusansmonoB.GSUBdata.php | 191 + .../mpdf/ttfontdata/dejavusansmonoB.cw.dat | Bin 0 -> 131072 bytes .../mpdf/ttfontdata/dejavusansmonoB.gid.dat | Bin 0 -> 196608 bytes .../mpdf/ttfontdata/dejavusansmonoB.mtx.php | 504 + .../ttfontdata/dejavuserifcondensed.cw.dat | Bin 0 -> 131072 bytes .../ttfontdata/dejavuserifcondensed.cw127.php | 142 + .../ttfontdata/dejavuserifcondensed.gid.dat | 0 .../ttfontdata/dejavuserifcondensed.mtx.php | 1716 + .../ttfontdata/dejavuserifcondensedB.cw.dat | Bin 0 -> 131072 bytes .../ttfontdata/dejavuserifcondensedB.gid.dat | 0 .../ttfontdata/dejavuserifcondensedB.mtx.php | 1763 + .../vendors/mpdf/ttfonts/DejaVuSans-Bold.ttf | Bin 0 -> 693876 bytes .../mpdf/ttfonts/DejaVuSans-BoldOblique.ttf | Bin 0 -> 632168 bytes .../mpdf/ttfonts/DejaVuSans-Oblique.ttf | Bin 0 -> 632416 bytes system/vendors/mpdf/ttfonts/DejaVuSans.ttf | Bin 0 -> 741536 bytes .../mpdf/ttfonts/DejaVuSansCondensed-Bold.ttf | Bin 0 -> 653336 bytes .../DejaVuSansCondensed-BoldOblique.ttf | Bin 0 -> 600936 bytes .../ttfonts/DejaVuSansCondensed-Oblique.ttf | Bin 0 -> 596596 bytes .../mpdf/ttfonts/DejaVuSansCondensed.ttf | Bin 0 -> 664972 bytes .../mpdf/ttfonts/DejaVuSansMono-Bold.ttf | Bin 0 -> 318392 bytes .../ttfonts/DejaVuSansMono-BoldOblique.ttf | Bin 0 -> 239876 bytes .../mpdf/ttfonts/DejaVuSansMono-Oblique.ttf | Bin 0 -> 245948 bytes .../vendors/mpdf/ttfonts/DejaVuSansMono.ttf | Bin 0 -> 335068 bytes .../vendors/mpdf/ttfonts/DejaVuSerif-Bold.ttf | Bin 0 -> 345364 bytes .../mpdf/ttfonts/DejaVuSerif-BoldItalic.ttf | Bin 0 -> 336884 bytes .../mpdf/ttfonts/DejaVuSerif-Italic.ttf | Bin 0 -> 343388 bytes system/vendors/mpdf/ttfonts/DejaVuSerif.ttf | Bin 0 -> 367260 bytes .../ttfonts/DejaVuSerifCondensed-Bold.ttf | Bin 0 -> 320720 bytes .../DejaVuSerifCondensed-BoldItalic.ttf | Bin 0 -> 335940 bytes .../ttfonts/DejaVuSerifCondensed-Italic.ttf | Bin 0 -> 342736 bytes .../mpdf/ttfonts/DejaVuSerifCondensed.ttf | Bin 0 -> 334040 bytes system/vendors/mpdf/ttfonts/DejaVuinfo.txt | 99 + system/vendors/mpdf/utils/UnicodeData.txt | 21488 ++++++++++ system/vendors/mpdf/utils/UnicodeRanges.php | 218 + .../vendors/mpdf/utils/font_collections.php | 72 + system/vendors/mpdf/utils/font_coverage.php | 252 + system/vendors/mpdf/utils/font_dump.php | 235 + system/vendors/mpdf/utils/font_dump_OTL.php | 237 + system/vendors/mpdf/utils/font_names.php | 214 + system/vendors/mpdf/utils/image_details.php | 525 + system/vendors/mpdf/utils/index.php | 29 + system/vendors/smarty/COPYING.lib | 165 + system/vendors/smarty/index.html | 8 + system/vendors/smarty/libs/.DS_Store | Bin 0 -> 6148 bytes system/vendors/smarty/libs/Smarty.class.php | 1528 + system/vendors/smarty/libs/SmartyBC.class.php | 460 + system/vendors/smarty/libs/debug.tpl | 133 + system/vendors/smarty/libs/index.html | 8 + .../smarty/libs/plugins/block.textformat.php | 113 + .../smarty/libs/plugins/function.counter.php | 78 + .../smarty/libs/plugins/function.cycle.php | 106 + .../smarty/libs/plugins/function.fetch.php | 214 + .../libs/plugins/function.html_checkboxes.php | 233 + .../libs/plugins/function.html_image.php | 159 + .../libs/plugins/function.html_options.php | 193 + .../libs/plugins/function.html_radios.php | 217 + .../plugins/function.html_select_date.php | 394 + .../plugins/function.html_select_time.php | 366 + .../libs/plugins/function.html_table.php | 177 + .../smarty/libs/plugins/function.mailto.php | 152 + .../smarty/libs/plugins/function.math.php | 87 + system/vendors/smarty/libs/plugins/index.html | 8 + .../libs/plugins/modifier.capitalize.php | 65 + .../libs/plugins/modifier.date_format.php | 65 + .../libs/plugins/modifier.debug_print_var.php | 105 + .../smarty/libs/plugins/modifier.escape.php | 188 + .../libs/plugins/modifier.regex_replace.php | 55 + .../smarty/libs/plugins/modifier.replace.php | 33 + .../smarty/libs/plugins/modifier.spacify.php | 27 + .../smarty/libs/plugins/modifier.truncate.php | 59 + .../libs/plugins/modifiercompiler.cat.php | 30 + .../modifiercompiler.count_characters.php | 33 + .../modifiercompiler.count_paragraphs.php | 28 + .../modifiercompiler.count_sentences.php | 28 + .../plugins/modifiercompiler.count_words.php | 32 + .../libs/plugins/modifiercompiler.default.php | 35 + .../libs/plugins/modifiercompiler.escape.php | 125 + .../plugins/modifiercompiler.from_charset.php | 34 + .../libs/plugins/modifiercompiler.indent.php | 32 + .../libs/plugins/modifiercompiler.lower.php | 31 + .../libs/plugins/modifiercompiler.noprint.php | 25 + .../modifiercompiler.string_format.php | 26 + .../libs/plugins/modifiercompiler.strip.php | 33 + .../plugins/modifiercompiler.strip_tags.php | 33 + .../plugins/modifiercompiler.to_charset.php | 34 + .../plugins/modifiercompiler.unescape.php | 51 + .../libs/plugins/modifiercompiler.upper.php | 30 + .../plugins/modifiercompiler.wordwrap.php | 46 + .../plugins/outputfilter.trimwhitespace.php | 94 + .../plugins/shared.escape_special_chars.php | 51 + .../plugins/shared.literal_compiler_param.php | 33 + .../libs/plugins/shared.make_timestamp.php | 42 + .../libs/plugins/shared.mb_str_replace.php | 55 + .../smarty/libs/plugins/shared.mb_unicode.php | 48 + .../libs/plugins/shared.mb_wordwrap.php | 83 + .../variablefilter.htmlspecialchars.php | 21 + .../vendors/smarty/libs/sysplugins/index.html | 8 + .../libs/sysplugins/smarty_cacheresource.php | 381 + .../smarty_cacheresource_custom.php | 237 + .../smarty_cacheresource_keyvaluestore.php | 463 + .../libs/sysplugins/smarty_config_source.php | 95 + .../smarty_internal_cacheresource_file.php | 266 + .../smarty_internal_compile_append.php | 53 + .../smarty_internal_compile_assign.php | 88 + .../smarty_internal_compile_block.php | 282 + .../smarty_internal_compile_break.php | 77 + .../smarty_internal_compile_call.php | 130 + .../smarty_internal_compile_capture.php | 98 + .../smarty_internal_compile_config_load.php | 85 + .../smarty_internal_compile_continue.php | 78 + .../smarty_internal_compile_debug.php | 43 + .../smarty_internal_compile_eval.php | 73 + .../smarty_internal_compile_extends.php | 133 + .../smarty_internal_compile_for.php | 151 + .../smarty_internal_compile_foreach.php | 231 + .../smarty_internal_compile_function.php | 166 + .../sysplugins/smarty_internal_compile_if.php | 207 + .../smarty_internal_compile_include.php | 215 + .../smarty_internal_compile_include_php.php | 108 + .../smarty_internal_compile_insert.php | 142 + .../smarty_internal_compile_ldelim.php | 41 + .../smarty_internal_compile_nocache.php | 73 + ..._internal_compile_private_block_plugin.php | 87 + ...ternal_compile_private_function_plugin.php | 73 + ...arty_internal_compile_private_modifier.php | 140 + ..._compile_private_object_block_function.php | 88 + ...ternal_compile_private_object_function.php | 79 + ...ernal_compile_private_print_expression.php | 156 + ...ernal_compile_private_registered_block.php | 113 + ...al_compile_private_registered_function.php | 81 + ...ernal_compile_private_special_variable.php | 111 + .../smarty_internal_compile_rdelim.php | 41 + .../smarty_internal_compile_section.php | 203 + .../smarty_internal_compile_setfilter.php | 72 + .../smarty_internal_compile_while.php | 94 + .../smarty_internal_compilebase.php | 176 + .../sysplugins/smarty_internal_config.php | 302 + .../smarty_internal_config_file_compiler.php | 144 + .../smarty_internal_configfilelexer.php | 622 + .../smarty_internal_configfileparser.php | 921 + .../libs/sysplugins/smarty_internal_data.php | 556 + .../libs/sysplugins/smarty_internal_debug.php | 206 + .../smarty_internal_filter_handler.php | 70 + .../smarty_internal_function_call_handler.php | 55 + .../smarty_internal_get_include_path.php | 48 + .../smarty_internal_nocache_insert.php | 53 + .../sysplugins/smarty_internal_parsetree.php | 395 + .../smarty_internal_resource_eval.php | 94 + .../smarty_internal_resource_extends.php | 162 + .../smarty_internal_resource_file.php | 90 + .../smarty_internal_resource_php.php | 114 + .../smarty_internal_resource_registered.php | 95 + .../smarty_internal_resource_stream.php | 78 + .../smarty_internal_resource_string.php | 96 + ...smarty_internal_smartytemplatecompiler.php | 127 + .../sysplugins/smarty_internal_template.php | 692 + .../smarty_internal_templatebase.php | 811 + .../smarty_internal_templatecompilerbase.php | 670 + .../smarty_internal_templatelexer.php | 1203 + .../smarty_internal_templateparser.php | 3254 ++ .../sysplugins/smarty_internal_utility.php | 830 + .../sysplugins/smarty_internal_write_file.php | 88 + .../libs/sysplugins/smarty_resource.php | 857 + .../sysplugins/smarty_resource_custom.php | 96 + .../sysplugins/smarty_resource_recompiled.php | 36 + .../sysplugins/smarty_resource_uncompiled.php | 44 + .../libs/sysplugins/smarty_security.php | 459 + ui/.DS_Store | Bin 0 -> 6148 bytes ui/cache/index.html | 8 + ui/compiled/index.html | 8 + ui/conf/index.html | 8 + ui/index.html | 8 + ui/lib/c/bandwidth.js | 9 + ui/lib/c/customers.js | 9 + ui/lib/c/hotspot.js | 9 + ui/lib/c/index.html | 8 + ui/lib/c/pool.js | 9 + ui/lib/c/pppoe.js | 9 + ui/lib/c/prepaid.js | 9 + ui/lib/c/routers.js | 9 + ui/lib/c/users.js | 9 + ui/lib/c/voucher.js | 9 + ui/lib/css/bootstrap-rtl.min.css | 1 + ui/lib/css/index.html | 8 + ui/lib/dt/dataTables.bootstrap.js | 186 + ui/lib/dt/datatables.js | 407 + ui/lib/dt/index.html | 8 + ui/lib/dt/jquery.dataTables.js | 14951 +++++++ ui/lib/dt/jquery.dataTables.min.css | 333 + ui/lib/dt/jquery.dataTables.min.js | 158 + ui/lib/index.html | 8 + ui/lib/js/bootbox.min.js | 6 + ui/lib/js/index.html | 8 + ui/lib/js/select2/edit.js | 85 + ui/lib/js/select2/img/droparrows.png | Bin 0 -> 1110 bytes ui/lib/js/select2/img/select2-spinner.gif | Bin 0 -> 1849 bytes ui/lib/js/select2/img/select2.png | Bin 0 -> 613 bytes ui/lib/js/select2/img/select2x2.png | Bin 0 -> 845 bytes ui/lib/js/select2/select2.css | 730 + ui/lib/js/select2/select2.min.js | 23 + ui/theme/.DS_Store | Bin 0 -> 6148 bytes ui/theme/default/404.tpl | 10 + ui/theme/default/a404.tpl | 10 + ui/theme/default/admin.tpl | 72 + ui/theme/default/app-localisation.tpl | 80 + ui/theme/default/app-settings.tpl | 58 + ui/theme/default/autoload-pool.tpl | 4 + ui/theme/default/autoload-server.tpl | 4 + ui/theme/default/autoload.tpl | 4 + ui/theme/default/bandwidth-add.tpl | 54 + ui/theme/default/bandwidth-edit.tpl | 55 + ui/theme/default/bandwidth.tpl | 57 + ui/theme/default/change-password.tpl | 41 + ui/theme/default/customers-add.tpl | 61 + ui/theme/default/customers-edit.tpl | 62 + ui/theme/default/customers.tpl | 60 + ui/theme/default/dashboard.tpl | 180 + ui/theme/default/dbstatus.tpl | 43 + .../font-awesome/css/font-awesome.min.css | 4 + .../default/fonts/font-awesome/css/index.html | 8 + .../fonts/fontawesome-webfontd41d.eot | Bin 0 -> 60767 bytes .../fonts/fontawesome-webfonte0a5.eot | Bin 0 -> 60767 bytes .../fonts/fontawesome-webfonte0a5.svg | 565 + .../fonts/fontawesome-webfonte0a5.ttf | Bin 0 -> 122092 bytes .../fonts/fontawesome-webfonte0a5.woff | Bin 0 -> 71508 bytes .../fonts/fontawesome-webfonte0a5.woff2 | Bin 0 -> 56780 bytes .../fonts/font-awesome/fonts/index.html | 8 + .../default/fonts/font-awesome/index.html | 8 + ui/theme/default/fonts/index.html | 8 + .../default/fonts/ionicons/css/index.html | 8 + .../fonts/ionicons/css/ionicons.min.css | 11 + .../default/fonts/ionicons/fonts/index.html | 8 + .../fonts/ionicons/fonts/ionicons28b5.eot | Bin 0 -> 120724 bytes .../fonts/ionicons/fonts/ionicons28b5.svg | 2230 ++ .../fonts/ionicons/fonts/ionicons28b5.ttf | Bin 0 -> 188508 bytes .../fonts/ionicons/fonts/ionicons28b5.woff | Bin 0 -> 67904 bytes ui/theme/default/fonts/ionicons/index.html | 8 + ui/theme/default/hotspot-add.tpl | 120 + ui/theme/default/hotspot-edit.tpl | 116 + ui/theme/default/hotspot.tpl | 67 + ui/theme/default/images/admin.png | Bin 0 -> 1914 bytes ui/theme/default/images/index.html | 8 + ui/theme/default/images/logo.png | Bin 0 -> 6761 bytes ui/theme/default/index.html | 8 + ui/theme/default/invoice-print.tpl | 58 + ui/theme/default/invoice.tpl | 48 + ui/theme/default/language-add.tpl | 39 + ui/theme/default/login.tpl | 72 + ui/theme/default/pool-add.tpl | 45 + ui/theme/default/pool-edit.tpl | 44 + ui/theme/default/pool.tpl | 59 + ui/theme/default/pppoe-add.tpl | 76 + ui/theme/default/pppoe-edit.tpl | 73 + ui/theme/default/pppoe.tpl | 62 + ui/theme/default/prepaid-edit.tpl | 59 + ui/theme/default/prepaid.tpl | 65 + ui/theme/default/print-by-date.tpl | 72 + ui/theme/default/print-by-period.tpl | 72 + ui/theme/default/recharge-user.tpl | 62 + ui/theme/default/recharge.tpl | 57 + ui/theme/default/refill.tpl | 39 + ui/theme/default/reports-daily.tpl | 61 + ui/theme/default/reports-period-view.tpl | 69 + ui/theme/default/reports-period.tpl | 49 + ui/theme/default/routers-add.tpl | 55 + ui/theme/default/routers-edit.tpl | 55 + ui/theme/default/routers.tpl | 61 + ui/theme/default/scripts/app.js | 160 + ui/theme/default/scripts/bootstrap.js | 2363 ++ ui/theme/default/scripts/bootstrap.min.js | 7 + ui/theme/default/scripts/c3.init.js | 1 + ui/theme/default/scripts/calendar.init.js | 1 + ui/theme/default/scripts/custom.js | 106 + .../default/scripts/form-elements.init.js | 35 + ui/theme/default/scripts/inbox.init.js | 1 + ui/theme/default/scripts/index.html | 8 + ui/theme/default/scripts/index.init.js | 1 + ui/theme/default/scripts/jquery-1.10.2.js | 6 + ui/theme/default/scripts/maps.init.js | 1 + .../plugins/bootstrap-datepicker.min.js | 2 + .../scripts/plugins/bootstrap-rating.min.js | 1 + .../scripts/plugins/bootstrap-slider.min.js | 1 + ui/theme/default/scripts/plugins/c3.min.js | 5 + ui/theme/default/scripts/plugins/d3.min.js | 5 + .../scripts/plugins/fullcalendar.min.js | 3 + ui/theme/default/scripts/plugins/index.html | 8 + .../scripts/plugins/jquery.dataTables.min.js | 3 + .../plugins/jquery.easypiechart.min.js | 1 + .../scripts/plugins/jquery.sparkline.min.js | 2 + .../default/scripts/plugins/moment.min.js | 2 + .../scripts/plugins/perfect-scrollbar.min.js | 1 + .../default/scripts/plugins/screenfull.js | 37 + .../default/scripts/plugins/select2.min.js | 2 + .../default/scripts/plugins/summernote.min.js | 3 + ui/theme/default/scripts/plugins/waves.min.js | 1 + ui/theme/default/scripts/sparklines.init.js | 1 + ui/theme/default/scripts/tables.init.js | 1 + ui/theme/default/scripts/vendors.js | 98 + ui/theme/default/sections/footer.tpl | 63 + ui/theme/default/sections/header.tpl | 270 + ui/theme/default/sections/index.html | 8 + ui/theme/default/sections/user-footer.tpl | 64 + ui/theme/default/sections/user-header.tpl | 168 + ui/theme/default/styles/bootstrap.min.css | 5740 +++ ui/theme/default/styles/index.html | 8 + ui/theme/default/styles/main.min.css | 5901 +++ .../styles/plugins/bootstrap-colorpicker.css | 227 + .../styles/plugins/bootstrap-datepicker.css | 473 + .../styles/plugins/bootstrap-slider.css | 147 + ui/theme/default/styles/plugins/c3.css | 158 + .../default/styles/plugins/fullcalendar.css | 732 + ui/theme/default/styles/plugins/index.html | 8 + .../styles/plugins/perfect-scrollbar.css | 70 + ui/theme/default/styles/plugins/select2.css | 281 + .../default/styles/plugins/summernote.css | 469 + ui/theme/default/styles/plugins/waves.css | 129 + ui/theme/default/user-activation-list.tpl | 40 + ui/theme/default/user-activation.tpl | 28 + ui/theme/default/user-change-password.tpl | 41 + ui/theme/default/user-dashboard.tpl | 49 + ui/theme/default/user-profile.tpl | 50 + ui/theme/default/users-add.tpl | 58 + ui/theme/default/users-edit.tpl | 62 + ui/theme/default/users.tpl | 60 + ui/theme/default/voucher-add.tpl | 60 + ui/theme/default/voucher.tpl | 62 + ui/theme/index.html | 8 + ui/theme/theme1/index.html | 8 + 767 files changed, 239450 insertions(+) create mode 100644 .DS_Store create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 admin/index.php create mode 100644 index.php create mode 100644 system/.DS_Store create mode 100644 system/autoload/Admin.php create mode 100644 system/autoload/App.php create mode 100644 system/autoload/PEAR2/Autoload.php create mode 100644 system/autoload/PEAR2/Cache/SHM.php create mode 100644 system/autoload/PEAR2/Cache/SHM/Adapter/APC.php create mode 100644 system/autoload/PEAR2/Cache/SHM/Adapter/Placebo.php create mode 100644 system/autoload/PEAR2/Cache/SHM/Adapter/Wincache.php create mode 100644 system/autoload/PEAR2/Cache/SHM/Exception.php create mode 100644 system/autoload/PEAR2/Cache/SHM/InvalidArgumentException.php create mode 100644 system/autoload/PEAR2/Console/Color.php create mode 100644 system/autoload/PEAR2/Console/Color/Backgrounds.php create mode 100644 system/autoload/PEAR2/Console/Color/Exception.php create mode 100644 system/autoload/PEAR2/Console/Color/Flags.php create mode 100644 system/autoload/PEAR2/Console/Color/Fonts.php create mode 100644 system/autoload/PEAR2/Console/Color/Styles.php create mode 100644 system/autoload/PEAR2/Console/Color/UnexpectedValueException.php create mode 100644 system/autoload/PEAR2/Console/CommandLine.php create mode 100644 system/autoload/PEAR2/Console/CommandLine/Action.php create mode 100644 system/autoload/PEAR2/Console/CommandLine/Action/Callback.php create mode 100644 system/autoload/PEAR2/Console/CommandLine/Action/Counter.php create mode 100644 system/autoload/PEAR2/Console/CommandLine/Action/Help.php create mode 100644 system/autoload/PEAR2/Console/CommandLine/Action/List.php create mode 100644 system/autoload/PEAR2/Console/CommandLine/Action/Password.php create mode 100644 system/autoload/PEAR2/Console/CommandLine/Action/StoreArray.php create mode 100644 system/autoload/PEAR2/Console/CommandLine/Action/StoreFalse.php create mode 100644 system/autoload/PEAR2/Console/CommandLine/Action/StoreFloat.php create mode 100644 system/autoload/PEAR2/Console/CommandLine/Action/StoreInt.php create mode 100644 system/autoload/PEAR2/Console/CommandLine/Action/StoreString.php create mode 100644 system/autoload/PEAR2/Console/CommandLine/Action/StoreTrue.php create mode 100644 system/autoload/PEAR2/Console/CommandLine/Action/Version.php create mode 100644 system/autoload/PEAR2/Console/CommandLine/Argument.php create mode 100644 system/autoload/PEAR2/Console/CommandLine/Command.php create mode 100644 system/autoload/PEAR2/Console/CommandLine/CustomMessageProvider.php create mode 100644 system/autoload/PEAR2/Console/CommandLine/Element.php create mode 100644 system/autoload/PEAR2/Console/CommandLine/Exception.php create mode 100644 system/autoload/PEAR2/Console/CommandLine/MessageProvider.php create mode 100644 system/autoload/PEAR2/Console/CommandLine/MessageProvider/Default.php create mode 100644 system/autoload/PEAR2/Console/CommandLine/Option.php create mode 100644 system/autoload/PEAR2/Console/CommandLine/Outputter.php create mode 100644 system/autoload/PEAR2/Console/CommandLine/Outputter/Default.php create mode 100644 system/autoload/PEAR2/Console/CommandLine/Renderer.php create mode 100644 system/autoload/PEAR2/Console/CommandLine/Renderer/Default.php create mode 100644 system/autoload/PEAR2/Console/CommandLine/Result.php create mode 100644 system/autoload/PEAR2/Console/CommandLine/XmlParser.php create mode 100644 system/autoload/PEAR2/Net/RouterOS/Client.php create mode 100644 system/autoload/PEAR2/Net/RouterOS/Communicator.php create mode 100644 system/autoload/PEAR2/Net/RouterOS/DataFlowException.php create mode 100644 system/autoload/PEAR2/Net/RouterOS/Exception.php create mode 100644 system/autoload/PEAR2/Net/RouterOS/InvalidArgumentException.php create mode 100644 system/autoload/PEAR2/Net/RouterOS/LengthException.php create mode 100644 system/autoload/PEAR2/Net/RouterOS/Message.php create mode 100644 system/autoload/PEAR2/Net/RouterOS/NotSupportedException.php create mode 100644 system/autoload/PEAR2/Net/RouterOS/Query.php create mode 100644 system/autoload/PEAR2/Net/RouterOS/Registry.php create mode 100644 system/autoload/PEAR2/Net/RouterOS/Request.php create mode 100644 system/autoload/PEAR2/Net/RouterOS/Response.php create mode 100644 system/autoload/PEAR2/Net/RouterOS/ResponseCollection.php create mode 100644 system/autoload/PEAR2/Net/RouterOS/SocketException.php create mode 100644 system/autoload/PEAR2/Net/RouterOS/UnexpectedValueException.php create mode 100644 system/autoload/PEAR2/Net/RouterOS/Util.php create mode 100644 system/autoload/PEAR2/Net/Transmitter/Exception.php create mode 100644 system/autoload/PEAR2/Net/Transmitter/FilterCollection.php create mode 100644 system/autoload/PEAR2/Net/Transmitter/LockException.php create mode 100644 system/autoload/PEAR2/Net/Transmitter/NetworkStream.php create mode 100644 system/autoload/PEAR2/Net/Transmitter/SocketException.php create mode 100644 system/autoload/PEAR2/Net/Transmitter/Stream.php create mode 100644 system/autoload/PEAR2/Net/Transmitter/StreamException.php create mode 100644 system/autoload/PEAR2/Net/Transmitter/TcpClient.php create mode 100644 system/autoload/PEAR2/Net/Transmitter/TcpServerConnection.php create mode 100644 system/autoload/Paginator.php create mode 100644 system/autoload/Password.php create mode 100644 system/autoload/Router.php create mode 100644 system/autoload/Timezone.php create mode 100644 system/autoload/User.php create mode 100644 system/autoload/Validator.php create mode 100644 system/autoload/index.html create mode 100644 system/boot.php create mode 100644 system/config.sample.php create mode 100644 system/controllers/accounts.php create mode 100644 system/controllers/admin.php create mode 100644 system/controllers/autoload.php create mode 100644 system/controllers/bandwidth.php create mode 100644 system/controllers/customers.php create mode 100644 system/controllers/dashboard.php create mode 100644 system/controllers/default.php create mode 100644 system/controllers/export.php create mode 100644 system/controllers/home.php create mode 100644 system/controllers/index.html create mode 100644 system/controllers/login.php create mode 100644 system/controllers/logout.php create mode 100644 system/controllers/message.php create mode 100644 system/controllers/order.php create mode 100644 system/controllers/pm.php create mode 100644 system/controllers/pool.php create mode 100644 system/controllers/prepaid.php create mode 100644 system/controllers/reports.php create mode 100644 system/controllers/routers.php create mode 100644 system/controllers/services.php create mode 100644 system/controllers/settings.php create mode 100644 system/controllers/voucher.php create mode 100644 system/cron.php create mode 100644 system/index.html create mode 100644 system/install/.DS_Store create mode 100644 system/install/css/bootstrap.css create mode 100644 system/install/css/bootstrap.css.map create mode 100644 system/install/css/bootstrap.min.css create mode 100644 system/install/css/style.css create mode 100644 system/install/img/favicon.png create mode 100644 system/install/img/logo.png create mode 100644 system/install/index.php create mode 100644 system/install/phpmixbill.sql create mode 100644 system/install/step2.php create mode 100644 system/install/step3.php create mode 100644 system/install/step4.php create mode 100644 system/install/step5.php create mode 100644 system/lan/.DS_Store create mode 100644 system/lan/english/common.lan.php create mode 100644 system/lan/english/index.html create mode 100644 system/lan/index.html create mode 100644 system/lan/indonesia/common.lan.php create mode 100644 system/lan/indonesia/index.html create mode 100644 system/orm.php create mode 100644 system/uploads/.DS_Store create mode 100644 system/uploads/_sysfrm_tmp_/index.html create mode 100644 system/uploads/admin.png create mode 100644 system/uploads/index.html create mode 100644 system/uploads/logo.png create mode 100644 system/uploads/system/index.html create mode 100644 system/uploads/user.jpg create mode 100644 system/vendors/.DS_Store create mode 100644 system/vendors/index.html create mode 100644 system/vendors/mpdf/CHANGELOG.txt create mode 100644 system/vendors/mpdf/CREDITS.txt create mode 100644 system/vendors/mpdf/LICENSE.txt create mode 100644 system/vendors/mpdf/README.txt create mode 100644 system/vendors/mpdf/classes/barcode.php create mode 100644 system/vendors/mpdf/classes/bmp.php create mode 100644 system/vendors/mpdf/classes/cssmgr.php create mode 100644 system/vendors/mpdf/classes/desktop.ini create mode 100644 system/vendors/mpdf/classes/directw.php create mode 100644 system/vendors/mpdf/classes/gif.php create mode 100644 system/vendors/mpdf/classes/grad.php create mode 100644 system/vendors/mpdf/classes/indic.php create mode 100644 system/vendors/mpdf/classes/meter.php create mode 100644 system/vendors/mpdf/classes/mpdfform.php create mode 100644 system/vendors/mpdf/classes/myanmar.php create mode 100644 system/vendors/mpdf/classes/otl.php create mode 100644 system/vendors/mpdf/classes/otl_dump.php create mode 100644 system/vendors/mpdf/classes/sea.php create mode 100644 system/vendors/mpdf/classes/svg.php create mode 100644 system/vendors/mpdf/classes/tocontents.php create mode 100644 system/vendors/mpdf/classes/ttfontsuni.php create mode 100644 system/vendors/mpdf/classes/ttfontsuni_analysis.php create mode 100644 system/vendors/mpdf/classes/ucdn.php create mode 100644 system/vendors/mpdf/classes/wmf.php create mode 100644 system/vendors/mpdf/collations/Afrikaans_South_Africa.php create mode 100644 system/vendors/mpdf/collations/Albanian_Albania.php create mode 100644 system/vendors/mpdf/collations/Alsatian_France.php create mode 100644 system/vendors/mpdf/collations/Arabic_Algeria.php create mode 100644 system/vendors/mpdf/collations/Arabic_Bahrain.php create mode 100644 system/vendors/mpdf/collations/Arabic_Egypt.php create mode 100644 system/vendors/mpdf/collations/Arabic_Iraq.php create mode 100644 system/vendors/mpdf/collations/Arabic_Jordan.php create mode 100644 system/vendors/mpdf/collations/Arabic_Kuwait.php create mode 100644 system/vendors/mpdf/collations/Arabic_Lebanon.php create mode 100644 system/vendors/mpdf/collations/Arabic_Libya.php create mode 100644 system/vendors/mpdf/collations/Arabic_Morocco.php create mode 100644 system/vendors/mpdf/collations/Arabic_Oman.php create mode 100644 system/vendors/mpdf/collations/Arabic_Pseudo_RTL.php create mode 100644 system/vendors/mpdf/collations/Arabic_Qatar.php create mode 100644 system/vendors/mpdf/collations/Arabic_Saudi_Arabia.php create mode 100644 system/vendors/mpdf/collations/Arabic_Syria.php create mode 100644 system/vendors/mpdf/collations/Arabic_Tunisia.php create mode 100644 system/vendors/mpdf/collations/Arabic_Yemen.php create mode 100644 system/vendors/mpdf/collations/Azeri_(Cyrillic)_Azerbaijan.php create mode 100644 system/vendors/mpdf/collations/Azeri_(Latin)_Azerbaijan.php create mode 100644 system/vendors/mpdf/collations/Bashkir_Russia.php create mode 100644 system/vendors/mpdf/collations/Basque_Spain.php create mode 100644 system/vendors/mpdf/collations/Belarusian_Belarus.php create mode 100644 system/vendors/mpdf/collations/Bosnian_(Cyrillic)_Bosnia_and_Herzegovina.php create mode 100644 system/vendors/mpdf/collations/Bosnian_(Latin)_Bosnia_and_Herzegovina.php create mode 100644 system/vendors/mpdf/collations/Breton_France.php create mode 100644 system/vendors/mpdf/collations/Bulgarian_Bulgaria.php create mode 100644 system/vendors/mpdf/collations/Catalan_Spain.php create mode 100644 system/vendors/mpdf/collations/Corsican_France.php create mode 100644 system/vendors/mpdf/collations/Croatian_(Latin)_Bosnia_and_Herzegovina.php create mode 100644 system/vendors/mpdf/collations/Croatian_Croatia.php create mode 100644 system/vendors/mpdf/collations/Czech_Czech_Republic.php create mode 100644 system/vendors/mpdf/collations/Danish_Denmark.php create mode 100644 system/vendors/mpdf/collations/Dari_Afghanistan.php create mode 100644 system/vendors/mpdf/collations/Dutch_Belgium.php create mode 100644 system/vendors/mpdf/collations/Dutch_Netherlands.php create mode 100644 system/vendors/mpdf/collations/English_Australia.php create mode 100644 system/vendors/mpdf/collations/English_Belize.php create mode 100644 system/vendors/mpdf/collations/English_Canada.php create mode 100644 system/vendors/mpdf/collations/English_Caribbean.php create mode 100644 system/vendors/mpdf/collations/English_India.php create mode 100644 system/vendors/mpdf/collations/English_Ireland.php create mode 100644 system/vendors/mpdf/collations/English_Jamaica.php create mode 100644 system/vendors/mpdf/collations/English_Malaysia.php create mode 100644 system/vendors/mpdf/collations/English_New_Zealand.php create mode 100644 system/vendors/mpdf/collations/English_Republic_of_the_Philippines.php create mode 100644 system/vendors/mpdf/collations/English_Singapore.php create mode 100644 system/vendors/mpdf/collations/English_South_Africa.php create mode 100644 system/vendors/mpdf/collations/English_Trinidad_and_Tobago.php create mode 100644 system/vendors/mpdf/collations/English_United_Kingdom.php create mode 100644 system/vendors/mpdf/collations/English_United_States.php create mode 100644 system/vendors/mpdf/collations/English_Zimbabwe.php create mode 100644 system/vendors/mpdf/collations/Estonian_Estonia.php create mode 100644 system/vendors/mpdf/collations/Faroese_Faroe_Islands.php create mode 100644 system/vendors/mpdf/collations/Filipino_Philippines.php create mode 100644 system/vendors/mpdf/collations/Finnish_Finland.php create mode 100644 system/vendors/mpdf/collations/French_Belgium.php create mode 100644 system/vendors/mpdf/collations/French_Canada.php create mode 100644 system/vendors/mpdf/collations/French_France.php create mode 100644 system/vendors/mpdf/collations/French_Luxembourg.php create mode 100644 system/vendors/mpdf/collations/French_Principality_of_Monaco.php create mode 100644 system/vendors/mpdf/collations/French_Switzerland.php create mode 100644 system/vendors/mpdf/collations/Frisian_Netherlands.php create mode 100644 system/vendors/mpdf/collations/Galician_Spain.php create mode 100644 system/vendors/mpdf/collations/German_Austria.php create mode 100644 system/vendors/mpdf/collations/German_Germany.php create mode 100644 system/vendors/mpdf/collations/German_Liechtenstein.php create mode 100644 system/vendors/mpdf/collations/German_Luxembourg.php create mode 100644 system/vendors/mpdf/collations/German_Switzerland.php create mode 100644 system/vendors/mpdf/collations/Greek_Greece.php create mode 100644 system/vendors/mpdf/collations/Greenlandic_Greenland.php create mode 100644 system/vendors/mpdf/collations/Hausa_(Latin)_Nigeria.php create mode 100644 system/vendors/mpdf/collations/Hebrew_Israel.php create mode 100644 system/vendors/mpdf/collations/Hungarian_Hungary.php create mode 100644 system/vendors/mpdf/collations/Icelandic_Iceland.php create mode 100644 system/vendors/mpdf/collations/Igbo_Nigeria.php create mode 100644 system/vendors/mpdf/collations/Indonesian_Indonesia.php create mode 100644 system/vendors/mpdf/collations/Inuktitut_(Latin)_Canada.php create mode 100644 system/vendors/mpdf/collations/Invariant_Language_Invariant_Country.php create mode 100644 system/vendors/mpdf/collations/Irish_Ireland.php create mode 100644 system/vendors/mpdf/collations/Italian_Italy.php create mode 100644 system/vendors/mpdf/collations/Italian_Switzerland.php create mode 100644 system/vendors/mpdf/collations/Kinyarwanda_Rwanda.php create mode 100644 system/vendors/mpdf/collations/Kiswahili_Kenya.php create mode 100644 system/vendors/mpdf/collations/Kyrgyz_Kyrgyzstan.php create mode 100644 system/vendors/mpdf/collations/Latvian_Latvia.php create mode 100644 system/vendors/mpdf/collations/Lithuanian_Lithuania.php create mode 100644 system/vendors/mpdf/collations/Lower_Sorbian_Germany.php create mode 100644 system/vendors/mpdf/collations/Luxembourgish_Luxembourg.php create mode 100644 system/vendors/mpdf/collations/Macedonian_(FYROM)_Macedonia_(FYROM).php create mode 100644 system/vendors/mpdf/collations/Malay_Brunei_Darussalam.php create mode 100644 system/vendors/mpdf/collations/Malay_Malaysia.php create mode 100644 system/vendors/mpdf/collations/Mapudungun_Chile.php create mode 100644 system/vendors/mpdf/collations/Mohawk_Canada.php create mode 100644 system/vendors/mpdf/collations/Mongolian_(Cyrillic)_Mongolia.php create mode 100644 system/vendors/mpdf/collations/Norwegian_(Nynorsk)_Norway.php create mode 100644 system/vendors/mpdf/collations/Occitan_France.php create mode 100644 system/vendors/mpdf/collations/Persian_Iran.php create mode 100644 system/vendors/mpdf/collations/Polish_Poland.php create mode 100644 system/vendors/mpdf/collations/Portuguese_Brazil.php create mode 100644 system/vendors/mpdf/collations/Portuguese_Portugal.php create mode 100644 system/vendors/mpdf/collations/Quechua_Bolivia.php create mode 100644 system/vendors/mpdf/collations/Quechua_Ecuador.php create mode 100644 system/vendors/mpdf/collations/Quechua_Peru.php create mode 100644 system/vendors/mpdf/collations/Romanian_Romania.php create mode 100644 system/vendors/mpdf/collations/Romansh_Switzerland.php create mode 100644 system/vendors/mpdf/collations/Russian_Russia.php create mode 100644 system/vendors/mpdf/collations/Sami_(Inari)_Finland.php create mode 100644 system/vendors/mpdf/collations/Sami_(Lule)_Norway.php create mode 100644 system/vendors/mpdf/collations/Sami_(Lule)_Sweden.php create mode 100644 system/vendors/mpdf/collations/Sami_(Northern)_Finland.php create mode 100644 system/vendors/mpdf/collations/Sami_(Northern)_Norway.php create mode 100644 system/vendors/mpdf/collations/Sami_(Northern)_Sweden.php create mode 100644 system/vendors/mpdf/collations/Sami_(Skolt)_Finland.php create mode 100644 system/vendors/mpdf/collations/Sami_(Southern)_Norway.php create mode 100644 system/vendors/mpdf/collations/Sami_(Southern)_Sweden.php create mode 100644 system/vendors/mpdf/collations/Serbian_(Cyrillic)_Bosnia_and_Herzegovina.php create mode 100644 system/vendors/mpdf/collations/Serbian_(Cyrillic)_Serbia.php create mode 100644 system/vendors/mpdf/collations/Serbian_(Latin)_Bosnia_and_Herzegovina.php create mode 100644 system/vendors/mpdf/collations/Serbian_(Latin)_Serbia.php create mode 100644 system/vendors/mpdf/collations/Sesotho_sa_Leboa_South_Africa.php create mode 100644 system/vendors/mpdf/collations/Setswana_South_Africa.php create mode 100644 system/vendors/mpdf/collations/Slovak_Slovakia.php create mode 100644 system/vendors/mpdf/collations/Slovenian_Slovenia.php create mode 100644 system/vendors/mpdf/collations/Spanish_Argentina.php create mode 100644 system/vendors/mpdf/collations/Spanish_Bolivia.php create mode 100644 system/vendors/mpdf/collations/Spanish_Chile.php create mode 100644 system/vendors/mpdf/collations/Spanish_Colombia.php create mode 100644 system/vendors/mpdf/collations/Spanish_Costa_Rica.php create mode 100644 system/vendors/mpdf/collations/Spanish_Dominican_Republic.php create mode 100644 system/vendors/mpdf/collations/Spanish_Ecuador.php create mode 100644 system/vendors/mpdf/collations/Spanish_El_Salvador.php create mode 100644 system/vendors/mpdf/collations/Spanish_Guatemala.php create mode 100644 system/vendors/mpdf/collations/Spanish_Honduras.php create mode 100644 system/vendors/mpdf/collations/Spanish_Mexico.php create mode 100644 system/vendors/mpdf/collations/Spanish_Nicaragua.php create mode 100644 system/vendors/mpdf/collations/Spanish_Panama.php create mode 100644 system/vendors/mpdf/collations/Spanish_Paraguay.php create mode 100644 system/vendors/mpdf/collations/Spanish_Peru.php create mode 100644 system/vendors/mpdf/collations/Spanish_Puerto_Rico.php create mode 100644 system/vendors/mpdf/collations/Spanish_Spain.php create mode 100644 system/vendors/mpdf/collations/Spanish_United_States.php create mode 100644 system/vendors/mpdf/collations/Spanish_Uruguay.php create mode 100644 system/vendors/mpdf/collations/Spanish_Venezuela.php create mode 100644 system/vendors/mpdf/collations/Swedish_Finland.php create mode 100644 system/vendors/mpdf/collations/Swedish_Sweden.php create mode 100644 system/vendors/mpdf/collations/Tajik_(Cyrillic)_Tajikistan.php create mode 100644 system/vendors/mpdf/collations/Tamazight_(Latin)_Algeria.php create mode 100644 system/vendors/mpdf/collations/Tatar_Russia.php create mode 100644 system/vendors/mpdf/collations/Turkish_Turkey.php create mode 100644 system/vendors/mpdf/collations/Turkmen_Turkmenistan.php create mode 100644 system/vendors/mpdf/collations/Ukrainian_Ukraine.php create mode 100644 system/vendors/mpdf/collations/Upper_Sorbian_Germany.php create mode 100644 system/vendors/mpdf/collations/Urdu_Islamic_Republic_of_Pakistan.php create mode 100644 system/vendors/mpdf/collations/Uzbek_(Cyrillic)_Uzbekistan.php create mode 100644 system/vendors/mpdf/collations/Uzbek_(Latin)_Uzbekistan.php create mode 100644 system/vendors/mpdf/collations/Vietnamese_Vietnam.php create mode 100644 system/vendors/mpdf/collations/Welsh_United_Kingdom.php create mode 100644 system/vendors/mpdf/collations/Wolof_Senegal.php create mode 100644 system/vendors/mpdf/collations/Yakut_Russia.php create mode 100644 system/vendors/mpdf/collations/Yoruba_Nigeria.php create mode 100644 system/vendors/mpdf/collations/isiXhosa_South_Africa.php create mode 100644 system/vendors/mpdf/collations/isiZulu_South_Africa.php create mode 100644 system/vendors/mpdf/compress.php create mode 100644 system/vendors/mpdf/config.php create mode 100644 system/vendors/mpdf/config_fonts-distr-without-OTL.php create mode 100644 system/vendors/mpdf/config_fonts.php create mode 100644 system/vendors/mpdf/config_lang2fonts.php create mode 100644 system/vendors/mpdf/config_script2lang.php create mode 100644 system/vendors/mpdf/font/ccourier.php create mode 100644 system/vendors/mpdf/font/ccourierb.php create mode 100644 system/vendors/mpdf/font/ccourierbi.php create mode 100644 system/vendors/mpdf/font/ccourieri.php create mode 100644 system/vendors/mpdf/font/chelvetica.php create mode 100644 system/vendors/mpdf/font/chelveticab.php create mode 100644 system/vendors/mpdf/font/chelveticabi.php create mode 100644 system/vendors/mpdf/font/chelveticai.php create mode 100644 system/vendors/mpdf/font/csymbol.php create mode 100644 system/vendors/mpdf/font/ctimes.php create mode 100644 system/vendors/mpdf/font/ctimesb.php create mode 100644 system/vendors/mpdf/font/ctimesbi.php create mode 100644 system/vendors/mpdf/font/ctimesi.php create mode 100644 system/vendors/mpdf/font/czapfdingbats.php create mode 100644 system/vendors/mpdf/graph.php create mode 100644 system/vendors/mpdf/graph_cache/dummy.txt create mode 100644 system/vendors/mpdf/includes/CJKdata.php create mode 100644 system/vendors/mpdf/includes/functions.php create mode 100644 system/vendors/mpdf/includes/linebrdictK.dat create mode 100644 system/vendors/mpdf/includes/linebrdictL.dat create mode 100644 system/vendors/mpdf/includes/linebrdictT.dat create mode 100644 system/vendors/mpdf/includes/no_image.jpg create mode 100644 system/vendors/mpdf/includes/out.php create mode 100644 system/vendors/mpdf/includes/subs_core.php create mode 100644 system/vendors/mpdf/includes/subs_win-1252.php create mode 100644 system/vendors/mpdf/includes/upperCase.php create mode 100644 system/vendors/mpdf/lang2fonts.css create mode 100644 system/vendors/mpdf/mpdf.css create mode 100644 system/vendors/mpdf/mpdf.php create mode 100644 system/vendors/mpdf/mpdfi/filters/FilterASCII85.php create mode 100644 system/vendors/mpdf/mpdfi/filters/FilterLZW.php create mode 100644 system/vendors/mpdf/mpdfi/fpdi_pdf_parser.php create mode 100644 system/vendors/mpdf/mpdfi/pdf_context.php create mode 100644 system/vendors/mpdf/mpdfi/pdf_parser.php create mode 100644 system/vendors/mpdf/patterns/NOTES.txt create mode 100644 system/vendors/mpdf/patterns/de.php create mode 100644 system/vendors/mpdf/patterns/dictionary.txt create mode 100644 system/vendors/mpdf/patterns/en.php create mode 100644 system/vendors/mpdf/patterns/es.php create mode 100644 system/vendors/mpdf/patterns/fi.php create mode 100644 system/vendors/mpdf/patterns/fr.php create mode 100644 system/vendors/mpdf/patterns/it.php create mode 100644 system/vendors/mpdf/patterns/nl.php create mode 100644 system/vendors/mpdf/patterns/pl.php create mode 100644 system/vendors/mpdf/patterns/ru.php create mode 100644 system/vendors/mpdf/patterns/sv.php create mode 100644 system/vendors/mpdf/progbar.css create mode 100644 system/vendors/mpdf/tmp/dummy.txt create mode 100644 system/vendors/mpdf/ttfontdata/dejavusans.GDEFdata.php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusans.GPOSdata.php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusans.GSUB.arab.DFLT.php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusans.GSUB.arab.KUR .php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusans.GSUB.arab.SND .php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusans.GSUB.arab.URD .php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusans.GSUB.nko .DFLT.php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusans.GSUBGPOStables.dat create mode 100644 system/vendors/mpdf/ttfontdata/dejavusans.GSUBdata.php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusans.cw.dat create mode 100644 system/vendors/mpdf/ttfontdata/dejavusans.cw127.php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusans.gid.dat create mode 100644 system/vendors/mpdf/ttfontdata/dejavusans.mtx.php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusanscondensed.GDEFdata.php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusanscondensed.GPOSdata.php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusanscondensed.GSUB.arab.DFLT.php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusanscondensed.GSUB.arab.KUR .php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusanscondensed.GSUB.arab.SND .php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusanscondensed.GSUB.arab.URD .php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusanscondensed.GSUB.nko .DFLT.php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusanscondensed.GSUBGPOStables.dat create mode 100644 system/vendors/mpdf/ttfontdata/dejavusanscondensed.GSUBdata.php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusanscondensed.cw.dat create mode 100644 system/vendors/mpdf/ttfontdata/dejavusanscondensed.cw127.php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusanscondensed.gid.dat create mode 100644 system/vendors/mpdf/ttfontdata/dejavusanscondensed.mtx.php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusanscondensedB.GDEFdata.php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusanscondensedB.GPOSdata.php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusanscondensedB.GSUB.arab.DFLT.php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusanscondensedB.GSUB.arab.KUR .php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusanscondensedB.GSUB.arab.SND .php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusanscondensedB.GSUB.arab.URD .php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusanscondensedB.GSUB.nko .DFLT.php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusanscondensedB.GSUBGPOStables.dat create mode 100644 system/vendors/mpdf/ttfontdata/dejavusanscondensedB.GSUBdata.php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusanscondensedB.cw.dat create mode 100644 system/vendors/mpdf/ttfontdata/dejavusanscondensedB.cw127.php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusanscondensedB.gid.dat create mode 100644 system/vendors/mpdf/ttfontdata/dejavusanscondensedB.mtx.php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusanscondensedI.GDEFdata.php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusanscondensedI.GPOSdata.php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusanscondensedI.GSUBGPOStables.dat create mode 100644 system/vendors/mpdf/ttfontdata/dejavusanscondensedI.GSUBdata.php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusanscondensedI.cw.dat create mode 100644 system/vendors/mpdf/ttfontdata/dejavusanscondensedI.gid.dat create mode 100644 system/vendors/mpdf/ttfontdata/dejavusanscondensedI.mtx.php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusansmono.GDEFdata.php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusansmono.GPOSdata.php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusansmono.GSUB.arab.DFLT.php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusansmono.GSUBGPOStables.dat create mode 100644 system/vendors/mpdf/ttfontdata/dejavusansmono.GSUBdata.php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusansmono.cw.dat create mode 100644 system/vendors/mpdf/ttfontdata/dejavusansmono.cw127.php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusansmono.gid.dat create mode 100644 system/vendors/mpdf/ttfontdata/dejavusansmono.mtx.php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusansmonoB.GDEFdata.php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusansmonoB.GPOSdata.php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusansmonoB.GSUB.arab.DFLT.php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusansmonoB.GSUBGPOStables.dat create mode 100644 system/vendors/mpdf/ttfontdata/dejavusansmonoB.GSUBdata.php create mode 100644 system/vendors/mpdf/ttfontdata/dejavusansmonoB.cw.dat create mode 100644 system/vendors/mpdf/ttfontdata/dejavusansmonoB.gid.dat create mode 100644 system/vendors/mpdf/ttfontdata/dejavusansmonoB.mtx.php create mode 100644 system/vendors/mpdf/ttfontdata/dejavuserifcondensed.cw.dat create mode 100644 system/vendors/mpdf/ttfontdata/dejavuserifcondensed.cw127.php create mode 100644 system/vendors/mpdf/ttfontdata/dejavuserifcondensed.gid.dat create mode 100644 system/vendors/mpdf/ttfontdata/dejavuserifcondensed.mtx.php create mode 100644 system/vendors/mpdf/ttfontdata/dejavuserifcondensedB.cw.dat create mode 100644 system/vendors/mpdf/ttfontdata/dejavuserifcondensedB.gid.dat create mode 100644 system/vendors/mpdf/ttfontdata/dejavuserifcondensedB.mtx.php create mode 100644 system/vendors/mpdf/ttfonts/DejaVuSans-Bold.ttf create mode 100644 system/vendors/mpdf/ttfonts/DejaVuSans-BoldOblique.ttf create mode 100644 system/vendors/mpdf/ttfonts/DejaVuSans-Oblique.ttf create mode 100644 system/vendors/mpdf/ttfonts/DejaVuSans.ttf create mode 100644 system/vendors/mpdf/ttfonts/DejaVuSansCondensed-Bold.ttf create mode 100644 system/vendors/mpdf/ttfonts/DejaVuSansCondensed-BoldOblique.ttf create mode 100644 system/vendors/mpdf/ttfonts/DejaVuSansCondensed-Oblique.ttf create mode 100644 system/vendors/mpdf/ttfonts/DejaVuSansCondensed.ttf create mode 100644 system/vendors/mpdf/ttfonts/DejaVuSansMono-Bold.ttf create mode 100644 system/vendors/mpdf/ttfonts/DejaVuSansMono-BoldOblique.ttf create mode 100644 system/vendors/mpdf/ttfonts/DejaVuSansMono-Oblique.ttf create mode 100644 system/vendors/mpdf/ttfonts/DejaVuSansMono.ttf create mode 100644 system/vendors/mpdf/ttfonts/DejaVuSerif-Bold.ttf create mode 100644 system/vendors/mpdf/ttfonts/DejaVuSerif-BoldItalic.ttf create mode 100644 system/vendors/mpdf/ttfonts/DejaVuSerif-Italic.ttf create mode 100644 system/vendors/mpdf/ttfonts/DejaVuSerif.ttf create mode 100644 system/vendors/mpdf/ttfonts/DejaVuSerifCondensed-Bold.ttf create mode 100644 system/vendors/mpdf/ttfonts/DejaVuSerifCondensed-BoldItalic.ttf create mode 100644 system/vendors/mpdf/ttfonts/DejaVuSerifCondensed-Italic.ttf create mode 100644 system/vendors/mpdf/ttfonts/DejaVuSerifCondensed.ttf create mode 100644 system/vendors/mpdf/ttfonts/DejaVuinfo.txt create mode 100644 system/vendors/mpdf/utils/UnicodeData.txt create mode 100644 system/vendors/mpdf/utils/UnicodeRanges.php create mode 100644 system/vendors/mpdf/utils/font_collections.php create mode 100644 system/vendors/mpdf/utils/font_coverage.php create mode 100644 system/vendors/mpdf/utils/font_dump.php create mode 100644 system/vendors/mpdf/utils/font_dump_OTL.php create mode 100644 system/vendors/mpdf/utils/font_names.php create mode 100644 system/vendors/mpdf/utils/image_details.php create mode 100644 system/vendors/mpdf/utils/index.php create mode 100644 system/vendors/smarty/COPYING.lib create mode 100644 system/vendors/smarty/index.html create mode 100644 system/vendors/smarty/libs/.DS_Store create mode 100644 system/vendors/smarty/libs/Smarty.class.php create mode 100644 system/vendors/smarty/libs/SmartyBC.class.php create mode 100644 system/vendors/smarty/libs/debug.tpl create mode 100644 system/vendors/smarty/libs/index.html create mode 100644 system/vendors/smarty/libs/plugins/block.textformat.php create mode 100644 system/vendors/smarty/libs/plugins/function.counter.php create mode 100644 system/vendors/smarty/libs/plugins/function.cycle.php create mode 100644 system/vendors/smarty/libs/plugins/function.fetch.php create mode 100644 system/vendors/smarty/libs/plugins/function.html_checkboxes.php create mode 100644 system/vendors/smarty/libs/plugins/function.html_image.php create mode 100644 system/vendors/smarty/libs/plugins/function.html_options.php create mode 100644 system/vendors/smarty/libs/plugins/function.html_radios.php create mode 100644 system/vendors/smarty/libs/plugins/function.html_select_date.php create mode 100644 system/vendors/smarty/libs/plugins/function.html_select_time.php create mode 100644 system/vendors/smarty/libs/plugins/function.html_table.php create mode 100644 system/vendors/smarty/libs/plugins/function.mailto.php create mode 100644 system/vendors/smarty/libs/plugins/function.math.php create mode 100644 system/vendors/smarty/libs/plugins/index.html create mode 100644 system/vendors/smarty/libs/plugins/modifier.capitalize.php create mode 100644 system/vendors/smarty/libs/plugins/modifier.date_format.php create mode 100644 system/vendors/smarty/libs/plugins/modifier.debug_print_var.php create mode 100644 system/vendors/smarty/libs/plugins/modifier.escape.php create mode 100644 system/vendors/smarty/libs/plugins/modifier.regex_replace.php create mode 100644 system/vendors/smarty/libs/plugins/modifier.replace.php create mode 100644 system/vendors/smarty/libs/plugins/modifier.spacify.php create mode 100644 system/vendors/smarty/libs/plugins/modifier.truncate.php create mode 100644 system/vendors/smarty/libs/plugins/modifiercompiler.cat.php create mode 100644 system/vendors/smarty/libs/plugins/modifiercompiler.count_characters.php create mode 100644 system/vendors/smarty/libs/plugins/modifiercompiler.count_paragraphs.php create mode 100644 system/vendors/smarty/libs/plugins/modifiercompiler.count_sentences.php create mode 100644 system/vendors/smarty/libs/plugins/modifiercompiler.count_words.php create mode 100644 system/vendors/smarty/libs/plugins/modifiercompiler.default.php create mode 100644 system/vendors/smarty/libs/plugins/modifiercompiler.escape.php create mode 100644 system/vendors/smarty/libs/plugins/modifiercompiler.from_charset.php create mode 100644 system/vendors/smarty/libs/plugins/modifiercompiler.indent.php create mode 100644 system/vendors/smarty/libs/plugins/modifiercompiler.lower.php create mode 100644 system/vendors/smarty/libs/plugins/modifiercompiler.noprint.php create mode 100644 system/vendors/smarty/libs/plugins/modifiercompiler.string_format.php create mode 100644 system/vendors/smarty/libs/plugins/modifiercompiler.strip.php create mode 100644 system/vendors/smarty/libs/plugins/modifiercompiler.strip_tags.php create mode 100644 system/vendors/smarty/libs/plugins/modifiercompiler.to_charset.php create mode 100644 system/vendors/smarty/libs/plugins/modifiercompiler.unescape.php create mode 100644 system/vendors/smarty/libs/plugins/modifiercompiler.upper.php create mode 100644 system/vendors/smarty/libs/plugins/modifiercompiler.wordwrap.php create mode 100644 system/vendors/smarty/libs/plugins/outputfilter.trimwhitespace.php create mode 100644 system/vendors/smarty/libs/plugins/shared.escape_special_chars.php create mode 100644 system/vendors/smarty/libs/plugins/shared.literal_compiler_param.php create mode 100644 system/vendors/smarty/libs/plugins/shared.make_timestamp.php create mode 100644 system/vendors/smarty/libs/plugins/shared.mb_str_replace.php create mode 100644 system/vendors/smarty/libs/plugins/shared.mb_unicode.php create mode 100644 system/vendors/smarty/libs/plugins/shared.mb_wordwrap.php create mode 100644 system/vendors/smarty/libs/plugins/variablefilter.htmlspecialchars.php create mode 100644 system/vendors/smarty/libs/sysplugins/index.html create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_cacheresource.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_cacheresource_custom.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_cacheresource_keyvaluestore.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_config_source.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_cacheresource_file.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_compile_append.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_compile_assign.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_compile_block.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_compile_break.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_compile_call.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_compile_capture.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_compile_config_load.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_compile_continue.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_compile_debug.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_compile_eval.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_compile_extends.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_compile_for.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_compile_foreach.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_compile_function.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_compile_if.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_compile_include.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_compile_include_php.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_compile_insert.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_compile_ldelim.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_compile_nocache.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_compile_private_block_plugin.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_compile_private_function_plugin.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_compile_private_modifier.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_compile_private_object_block_function.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_compile_private_object_function.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_compile_private_print_expression.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_compile_private_registered_block.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_compile_private_registered_function.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_compile_private_special_variable.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_compile_rdelim.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_compile_section.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_compile_setfilter.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_compile_while.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_compilebase.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_config.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_config_file_compiler.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_configfilelexer.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_configfileparser.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_data.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_debug.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_filter_handler.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_function_call_handler.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_get_include_path.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_nocache_insert.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_parsetree.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_resource_eval.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_resource_extends.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_resource_file.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_resource_php.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_resource_registered.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_resource_stream.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_resource_string.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_smartytemplatecompiler.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_template.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_templatebase.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_templatecompilerbase.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_templatelexer.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_templateparser.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_utility.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_internal_write_file.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_resource.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_resource_custom.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_resource_recompiled.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_resource_uncompiled.php create mode 100644 system/vendors/smarty/libs/sysplugins/smarty_security.php create mode 100644 ui/.DS_Store create mode 100644 ui/cache/index.html create mode 100644 ui/compiled/index.html create mode 100644 ui/conf/index.html create mode 100644 ui/index.html create mode 100644 ui/lib/c/bandwidth.js create mode 100644 ui/lib/c/customers.js create mode 100644 ui/lib/c/hotspot.js create mode 100644 ui/lib/c/index.html create mode 100644 ui/lib/c/pool.js create mode 100644 ui/lib/c/pppoe.js create mode 100644 ui/lib/c/prepaid.js create mode 100644 ui/lib/c/routers.js create mode 100644 ui/lib/c/users.js create mode 100644 ui/lib/c/voucher.js create mode 100644 ui/lib/css/bootstrap-rtl.min.css create mode 100644 ui/lib/css/index.html create mode 100644 ui/lib/dt/dataTables.bootstrap.js create mode 100644 ui/lib/dt/datatables.js create mode 100644 ui/lib/dt/index.html create mode 100644 ui/lib/dt/jquery.dataTables.js create mode 100644 ui/lib/dt/jquery.dataTables.min.css create mode 100644 ui/lib/dt/jquery.dataTables.min.js create mode 100644 ui/lib/index.html create mode 100644 ui/lib/js/bootbox.min.js create mode 100644 ui/lib/js/index.html create mode 100644 ui/lib/js/select2/edit.js create mode 100644 ui/lib/js/select2/img/droparrows.png create mode 100644 ui/lib/js/select2/img/select2-spinner.gif create mode 100644 ui/lib/js/select2/img/select2.png create mode 100644 ui/lib/js/select2/img/select2x2.png create mode 100644 ui/lib/js/select2/select2.css create mode 100644 ui/lib/js/select2/select2.min.js create mode 100644 ui/theme/.DS_Store create mode 100644 ui/theme/default/404.tpl create mode 100644 ui/theme/default/a404.tpl create mode 100644 ui/theme/default/admin.tpl create mode 100644 ui/theme/default/app-localisation.tpl create mode 100644 ui/theme/default/app-settings.tpl create mode 100644 ui/theme/default/autoload-pool.tpl create mode 100644 ui/theme/default/autoload-server.tpl create mode 100644 ui/theme/default/autoload.tpl create mode 100644 ui/theme/default/bandwidth-add.tpl create mode 100644 ui/theme/default/bandwidth-edit.tpl create mode 100644 ui/theme/default/bandwidth.tpl create mode 100644 ui/theme/default/change-password.tpl create mode 100644 ui/theme/default/customers-add.tpl create mode 100644 ui/theme/default/customers-edit.tpl create mode 100644 ui/theme/default/customers.tpl create mode 100644 ui/theme/default/dashboard.tpl create mode 100644 ui/theme/default/dbstatus.tpl create mode 100644 ui/theme/default/fonts/font-awesome/css/font-awesome.min.css create mode 100644 ui/theme/default/fonts/font-awesome/css/index.html create mode 100644 ui/theme/default/fonts/font-awesome/fonts/fontawesome-webfontd41d.eot create mode 100644 ui/theme/default/fonts/font-awesome/fonts/fontawesome-webfonte0a5.eot create mode 100644 ui/theme/default/fonts/font-awesome/fonts/fontawesome-webfonte0a5.svg create mode 100644 ui/theme/default/fonts/font-awesome/fonts/fontawesome-webfonte0a5.ttf create mode 100644 ui/theme/default/fonts/font-awesome/fonts/fontawesome-webfonte0a5.woff create mode 100644 ui/theme/default/fonts/font-awesome/fonts/fontawesome-webfonte0a5.woff2 create mode 100644 ui/theme/default/fonts/font-awesome/fonts/index.html create mode 100644 ui/theme/default/fonts/font-awesome/index.html create mode 100644 ui/theme/default/fonts/index.html create mode 100644 ui/theme/default/fonts/ionicons/css/index.html create mode 100644 ui/theme/default/fonts/ionicons/css/ionicons.min.css create mode 100644 ui/theme/default/fonts/ionicons/fonts/index.html create mode 100644 ui/theme/default/fonts/ionicons/fonts/ionicons28b5.eot create mode 100644 ui/theme/default/fonts/ionicons/fonts/ionicons28b5.svg create mode 100644 ui/theme/default/fonts/ionicons/fonts/ionicons28b5.ttf create mode 100644 ui/theme/default/fonts/ionicons/fonts/ionicons28b5.woff create mode 100644 ui/theme/default/fonts/ionicons/index.html create mode 100644 ui/theme/default/hotspot-add.tpl create mode 100644 ui/theme/default/hotspot-edit.tpl create mode 100644 ui/theme/default/hotspot.tpl create mode 100644 ui/theme/default/images/admin.png create mode 100644 ui/theme/default/images/index.html create mode 100644 ui/theme/default/images/logo.png create mode 100644 ui/theme/default/index.html create mode 100644 ui/theme/default/invoice-print.tpl create mode 100644 ui/theme/default/invoice.tpl create mode 100644 ui/theme/default/language-add.tpl create mode 100644 ui/theme/default/login.tpl create mode 100644 ui/theme/default/pool-add.tpl create mode 100644 ui/theme/default/pool-edit.tpl create mode 100644 ui/theme/default/pool.tpl create mode 100644 ui/theme/default/pppoe-add.tpl create mode 100644 ui/theme/default/pppoe-edit.tpl create mode 100644 ui/theme/default/pppoe.tpl create mode 100644 ui/theme/default/prepaid-edit.tpl create mode 100644 ui/theme/default/prepaid.tpl create mode 100644 ui/theme/default/print-by-date.tpl create mode 100644 ui/theme/default/print-by-period.tpl create mode 100644 ui/theme/default/recharge-user.tpl create mode 100644 ui/theme/default/recharge.tpl create mode 100644 ui/theme/default/refill.tpl create mode 100644 ui/theme/default/reports-daily.tpl create mode 100644 ui/theme/default/reports-period-view.tpl create mode 100644 ui/theme/default/reports-period.tpl create mode 100644 ui/theme/default/routers-add.tpl create mode 100644 ui/theme/default/routers-edit.tpl create mode 100644 ui/theme/default/routers.tpl create mode 100644 ui/theme/default/scripts/app.js create mode 100644 ui/theme/default/scripts/bootstrap.js create mode 100644 ui/theme/default/scripts/bootstrap.min.js create mode 100644 ui/theme/default/scripts/c3.init.js create mode 100644 ui/theme/default/scripts/calendar.init.js create mode 100644 ui/theme/default/scripts/custom.js create mode 100644 ui/theme/default/scripts/form-elements.init.js create mode 100644 ui/theme/default/scripts/inbox.init.js create mode 100644 ui/theme/default/scripts/index.html create mode 100644 ui/theme/default/scripts/index.init.js create mode 100644 ui/theme/default/scripts/jquery-1.10.2.js create mode 100644 ui/theme/default/scripts/maps.init.js create mode 100644 ui/theme/default/scripts/plugins/bootstrap-datepicker.min.js create mode 100644 ui/theme/default/scripts/plugins/bootstrap-rating.min.js create mode 100644 ui/theme/default/scripts/plugins/bootstrap-slider.min.js create mode 100644 ui/theme/default/scripts/plugins/c3.min.js create mode 100644 ui/theme/default/scripts/plugins/d3.min.js create mode 100644 ui/theme/default/scripts/plugins/fullcalendar.min.js create mode 100644 ui/theme/default/scripts/plugins/index.html create mode 100644 ui/theme/default/scripts/plugins/jquery.dataTables.min.js create mode 100644 ui/theme/default/scripts/plugins/jquery.easypiechart.min.js create mode 100644 ui/theme/default/scripts/plugins/jquery.sparkline.min.js create mode 100644 ui/theme/default/scripts/plugins/moment.min.js create mode 100644 ui/theme/default/scripts/plugins/perfect-scrollbar.min.js create mode 100644 ui/theme/default/scripts/plugins/screenfull.js create mode 100644 ui/theme/default/scripts/plugins/select2.min.js create mode 100644 ui/theme/default/scripts/plugins/summernote.min.js create mode 100644 ui/theme/default/scripts/plugins/waves.min.js create mode 100644 ui/theme/default/scripts/sparklines.init.js create mode 100644 ui/theme/default/scripts/tables.init.js create mode 100644 ui/theme/default/scripts/vendors.js create mode 100644 ui/theme/default/sections/footer.tpl create mode 100644 ui/theme/default/sections/header.tpl create mode 100644 ui/theme/default/sections/index.html create mode 100644 ui/theme/default/sections/user-footer.tpl create mode 100644 ui/theme/default/sections/user-header.tpl create mode 100644 ui/theme/default/styles/bootstrap.min.css create mode 100644 ui/theme/default/styles/index.html create mode 100644 ui/theme/default/styles/main.min.css create mode 100644 ui/theme/default/styles/plugins/bootstrap-colorpicker.css create mode 100644 ui/theme/default/styles/plugins/bootstrap-datepicker.css create mode 100644 ui/theme/default/styles/plugins/bootstrap-slider.css create mode 100644 ui/theme/default/styles/plugins/c3.css create mode 100644 ui/theme/default/styles/plugins/fullcalendar.css create mode 100644 ui/theme/default/styles/plugins/index.html create mode 100644 ui/theme/default/styles/plugins/perfect-scrollbar.css create mode 100644 ui/theme/default/styles/plugins/select2.css create mode 100644 ui/theme/default/styles/plugins/summernote.css create mode 100644 ui/theme/default/styles/plugins/waves.css create mode 100644 ui/theme/default/user-activation-list.tpl create mode 100644 ui/theme/default/user-activation.tpl create mode 100644 ui/theme/default/user-change-password.tpl create mode 100644 ui/theme/default/user-dashboard.tpl create mode 100644 ui/theme/default/user-profile.tpl create mode 100644 ui/theme/default/users-add.tpl create mode 100644 ui/theme/default/users-edit.tpl create mode 100644 ui/theme/default/users.tpl create mode 100644 ui/theme/default/voucher-add.tpl create mode 100644 ui/theme/default/voucher.tpl create mode 100644 ui/theme/index.html create mode 100644 ui/theme/theme1/index.html diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..f528d5a3a664a059a61354e08ec63442ddfdf483 GIT binary patch literal 6148 zcmeH~!Ab)`42J*IDurHp^f<5J8w{mBL0_OkMGAHWwFPf`^R@kxNo94}s|Wc5nSYx} zX4vnrX#m*zw7&yZ02XvteEKpr-*+F`EF)J`i!-|O_O*L{>7GWpQvt8N#sl^^;(%d2 zPs9!0@IimaBaVZ!eQ$;n4!>ekwdt8KGG#rM6Pb5yM(J<0bQIYTvY#!*ckryxDR> zvACV*mq>@zrdmZn1b!0m*_*cV{(n!OnEyRTCITYxPYKw3v)!!ua@ku)uj#$E(0BBU qF*nk=L@TC7E9MVx#kYgJ;?LZ#seRKZXFSTO`Xk_6WFqhv1ik + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {description} + Copyright (C) {year} {fullname} + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + {signature of Ty Coon}, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..05de13e --- /dev/null +++ b/README.md @@ -0,0 +1,59 @@ +# PHP Mikrotik Billing +---- + +[![N|phpmixbill](http://4.bp.blogspot.com/-3OWL5OI7pqU/VjocUDdzMDI/AAAAAAAAAiA/s_XJN0_mDlk/s640/Screenshot_8.png) +www.phpmixbill.com +by Ismail Marzuqi + +New Features: +=================================================== +- New Coding (ORM & Smarty) +- New Design (responsive) +- NEW API Mikrotik (PEAR2_Net_RouterOS) +- Multi Router Mikrotik +- Hotspot & PPPOE +- Easy Installation +- Multi Language +and many more... + +STEPS: Installation +=================================================== +Auto Installer: +1. Unzip the contents of the zip file to a folder on your computer. +2. Upload the Entire phpmixbill_v5.0 folder to your website / server +3. Next you can rename the folder to whatever you like (billing, finance, manage etc..) +4. Now visit the uploaded location using your web browser to run the installer process. +5. Follow the instructions on screen to install PHPMixBill v5.0. +6. For security, Delete the install directory inside system folder. +7. If you see blank page after installation, it might be your compiled folder permissoon is not writable. Please make permission 755 compiled directory inside ui folder to store the generated contents from theme. + +Manual Install: +To install manually, follow this steps- + +1. Unzip the contents of the zip file to a folder on your computer. +2. Upload the Entire phpmixbill_v5.0 folder to your website / server +3. Next you can rename the folder to whatever you like (billing, finance, manage etc..) +4. Sample config file is available here- system/config.sample.php . Rename it to config.php & put it in same location (/system/config.php) Open config file using a text editor & Put the database info and url. +5. Import database. Database file is located here- system/install/phpmixbill.sql +6. For security, Delete the install directory inside sysfrm folder. + +System Requirements +==================================================== +Most current web servers with PHP & MySQL installed will be capable of running PHPMixBill v5.0. + +Minimum Requirements +- Linux or Windows OS +- PHP Version 5.3+ +- Both PDO & MySQLi Support +- GD2 Image Library +- MySQL Version 4.1.x and above + +copyright +---- +(C) 2014-2015 PHP Mikrotik Billing + +License +---- + +GNU General Public License version 2 or later; see LICENSE.txt +@donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4 \ No newline at end of file diff --git a/admin/index.php b/admin/index.php new file mode 100644 index 0000000..3967626 --- /dev/null +++ b/admin/index.php @@ -0,0 +1,11 @@ + +* @version 5.0 +* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing +* @license GNU General Public License version 2 or later; see LICENSE.txt +* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4 +**/ + +header('location: ../index.php?_route=admin/'); \ No newline at end of file diff --git a/index.php b/index.php new file mode 100644 index 0000000..d4614ef --- /dev/null +++ b/index.php @@ -0,0 +1,12 @@ + +* @version 5.0 +* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing +* @license GNU General Public License version 2 or later; see LICENSE.txt +* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4 +**/ + +require ('system/boot.php'); +App::_run(); diff --git a/system/.DS_Store b/system/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..98fad2e07fb966d5eaa896c0b5359df39877b371 GIT binary patch literal 6148 zcmeHKOKQU~5S?j4W5_1UE_H?8z=ZS!xj;-^O2H^4zqO9uH=l$!(YmB%1kxMLS9)N{ zA|hTM`&Xg`5jE(7EJCG;x@vlI=Vu^mj)At+QGZ(RawX9ts$}gA)_kM`)_%4AVVgf~ zUo@w8*fKnJ>NC&>q`mC&%4U+)pw zj5P%V!9Xw&4EzNKaA!+v=7uo_1HnKraA82shlDQJIu=7cIxy%H062wO8)SJ)NKA5U z9g86>Xl$WC3uS*|u!TdP+^=;kh89ljj}P|B=f?|cb>vU#PTU&C7z_jhV+JNRUC90a zhEJyY$RCHqEEos|{uu*2U9DG3Y|8J}yY0zco6tAt(!{TbfChQ=6M%`FBQI(+dlDIb Xtz$8iETY$NVEhOuAu$94zrer`$^kSd literal 0 HcmV?d00001 diff --git a/system/autoload/Admin.php b/system/autoload/Admin.php new file mode 100644 index 0000000..1ab3203 --- /dev/null +++ b/system/autoload/Admin.php @@ -0,0 +1,17 @@ + +* @version 5.0 +* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing +* @license GNU General Public License version 2 or later; see LICENSE.txt +* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4 +**/ + +Class Admin{ + public static function _info(){ + $id = $_SESSION['aid']; + $d = ORM::for_table('tbl_users')->find_one($id); + return $d; + } +} \ No newline at end of file diff --git a/system/autoload/App.php b/system/autoload/App.php new file mode 100644 index 0000000..706b32c --- /dev/null +++ b/system/autoload/App.php @@ -0,0 +1,16 @@ + +* @version 5.0 +* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing +* @license GNU General Public License version 2 or later; see LICENSE.txt +* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4 +**/ + +Class App{ + public static function _run(){ + return true; + } + +} \ No newline at end of file diff --git a/system/autoload/PEAR2/Autoload.php b/system/autoload/PEAR2/Autoload.php new file mode 100644 index 0000000..3d6161e --- /dev/null +++ b/system/autoload/PEAR2/Autoload.php @@ -0,0 +1,243 @@ +getTrace(); + if (isset($trace[2]) && isset($trace[2]['function']) && + in_array($trace[2]['function'], array('class_exists', 'interface_exists'))) { + return false; + } + if (isset($trace[1]) && isset($trace[1]['function']) && + in_array($trace[1]['function'], array('class_exists', 'interface_exists'))) { + return false; + } + throw $e; + } + + /** + * Check if the requested class was loaded from the specified path + * + * @return bool + */ + protected static function loadSuccessful($class) + { + if (!class_exists($class, false) && !interface_exists($class, false)) { + return false; + } + return true; + } + + /** + * If possible, update the classmap file with newly-discovered + * mapping. + * + * @param string $class Class name discovered + * + * @param string $origin File where class was found + * + */ + protected static function updateMap($class, $origin) + { + if (is_writable(self::$mapfile) || is_writable(dirname(self::$mapfile))) { + self::$map[$class] = $origin; + file_put_contents(self::$mapfile, + '<'."?php\n" + . "// PEAR2\Autoload auto-generated classmap\n" + . "return " . var_export(self::$map, true) . ';', + LOCK_EX + ); + } + } + + /** + * return the array of paths PEAR2 autoload has registered + * + * @return array + */ + static function getPaths() + { + return self::$paths; + } + } +} +Autoload::initialize(dirname(__DIR__)); \ No newline at end of file diff --git a/system/autoload/PEAR2/Cache/SHM.php b/system/autoload/PEAR2/Cache/SHM.php new file mode 100644 index 0000000..02bec96 --- /dev/null +++ b/system/autoload/PEAR2/Cache/SHM.php @@ -0,0 +1,371 @@ + + * @copyright 2011 Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 0.1.3 + * @link http://pear2.php.net/PEAR2_Cache_SHM + */ + +/** + * The namespace declaration. + */ +namespace PEAR2\Cache; + +/** + * Used as a catch-all for adapter initialization. + */ +use Exception as E; + +/** + * Implements this class. + */ +use IteratorAggregate; + +/** + * Used on failures by this class. + */ +use PEAR2\Cache\SHM\InvalidArgumentException; + +/** + * Main class for this package. + * + * Automatically chooses an adapter based on the available extensions. + * + * @category Caching + * @package PEAR2_Cache_SHM + * @author Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Cache_SHM + */ +abstract class SHM implements IteratorAggregate +{ + /** + * @var array An array of adapter names that meet their requirements. + */ + private static $_adapters = array(); + + /** + * Creates a new shared memory storage. + * + * Estabilishes a separate persistent storage. Adapter is automatically + * chosen based on the available extensions. + * + * @param string $persistentId The ID for the storage. + * + * @return static|SHM A new instance of an SHM adapter (child of this + * class). + */ + final public static function factory($persistentId) + { + foreach (self::$_adapters as $adapter) { + try { + return new $adapter($persistentId); + } catch (E $e) { + //In case of a runtime error, try to fallback to other adapters. + } + } + throw new InvalidArgumentException( + 'No appropriate adapter available', + 1 + ); + } + + /** + * Checks if the adapter meets its requirements. + * + * @return bool TRUE on success, FALSE on failure. + */ + public static function isMeetingRequirements() + { + return true; + } + + /** + * Registers an adapter. + * + * Registers an SHM adapter, allowing you to call it with {@link factory()}. + * + * @param string $adapter FQCN of adapter. A valid adapter is one that + * extends this class. The class will be autoloaded if not already + * present. + * @param bool $prepend Whether to prepend this adapter into the list of + * possible adapters, instead of appending to it. + * + * @return bool TRUE on success, FALSE on failure. + */ + final public static function registerAdapter($adapter, $prepend = false) + { + if (class_exists($adapter, true) + && is_subclass_of($adapter, '\\' . __CLASS__) + && $adapter::isMeetingRequirements() + ) { + if ($prepend) { + self::$_adapters = array_merge( + array($adapter), + self::$_adapters + ); + } else { + self::$_adapters[] = $adapter; + } + return true; + } + return false; + } + + /** + * Adds a value to the shared memory storage. + * + * Adds a value to the storage if it doesn't exist, or fails if it does. + * + * @param string $key Name of key to associate the value with. + * @param mixed $value Value for the specified key. + * @param int $ttl Seconds to store the value. If set to 0 indicates no + * time limit. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function __invoke($key, $value, $ttl = 0) + { + return $this->add($key, $value, $ttl); + } + + /** + * Gets a value from the shared memory storage. + * + * This is a magic method, thanks to which any property you attempt to get + * the value of will be fetched from the adapter, treating the property name + * as the key of the value to get. + * + * @param string $key Name of key to get. + * + * @return mixed The current value of the specified key. + */ + public function __get($key) + { + return $this->get($key); + } + + /** + * Sets a value in the shared memory storage. + * + * This is a magic method, thanks to which any property you attempt to set + * the value of will be set by the adapter, treating the property name as + * the key of the value to set. The value is set without a TTL. + * + * @param string $key Name of key to associate the value with. + * @param mixed $value Value for the specified key. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function __set($key, $value) + { + return $this->set($key, $value); + } + + /** + * Checks if a specified key is in the storage. + * + * This is a magic method, thanks to which any property you call isset() on + * will be checked by the adapter, treating the property name as the key + * of the value to check. + * + * @param string $key Name of key to check. + * + * @return bool TRUE if the key is in the storage, FALSE otherwise. + */ + public function __isset($key) + { + return $this->exists($key); + } + + /** + * Deletes a value from the shared memory storage. + * + * This is a magic method, thanks to which any property you attempt to unset + * the value of will be unset by the adapter, treating the property name as + * the key of the value to delete. + * + * @param string $key Name of key to delete. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function __unset($key) + { + return $this->delete($key); + } + + /** + * Creates a new shared memory storage. + * + * Estabilishes a separate persistent storage. + * + * @param string $persistentId The ID for the storage. The storage will be + * reused if it exists, or created if it doesn't exist. Data and locks + * are namespaced by this ID. + */ + abstract public function __construct($persistentId); + + /** + * Obtains a named lock. + * + * @param string $key Name of the key to obtain. Note that $key may + * repeat for each distinct $persistentId. + * @param double $timeout If the lock can't be immediatly obtained, the + * script will block for at most the specified amount of seconds. + * Setting this to 0 makes lock obtaining non blocking, and setting it + * to NULL makes it block without a time limit. + * + * @return bool TRUE on success, FALSE on failure. + */ + abstract public function lock($key, $timeout = null); + + /** + * Releases a named lock. + * + * @param string $key Name of the key to release. Note that $key may + * repeat for each distinct $persistentId. + * + * @return bool TRUE on success, FALSE on failure. + */ + abstract public function unlock($key); + + /** + * Checks if a specified key is in the storage. + * + * @param string $key Name of key to check. + * + * @return bool TRUE if the key is in the storage, FALSE otherwise. + */ + abstract public function exists($key); + + /** + * Adds a value to the shared memory storage. + * + * Adds a value to the storage if it doesn't exist, or fails if it does. + * + * @param string $key Name of key to associate the value with. + * @param mixed $value Value for the specified key. + * @param int $ttl Seconds to store the value. If set to 0 indicates no + * time limit. + * + * @return bool TRUE on success, FALSE on failure. + */ + abstract public function add($key, $value, $ttl = 0); + + /** + * Sets a value in the shared memory storage. + * + * Adds a value to the storage if it doesn't exist, overwrites it otherwise. + * + * @param string $key Name of key to associate the value with. + * @param mixed $value Value for the specified key. + * @param int $ttl Seconds to store the value. If set to 0 indicates no + * time limit. + * + * @return bool TRUE on success, FALSE on failure. + */ + abstract public function set($key, $value, $ttl = 0); + + /** + * Gets a value from the shared memory storage. + * + * Gets the current value, or throws an exception if it's not stored. + * + * @param string $key Name of key to get the value of. + * + * @return mixed The current value of the specified key. + */ + abstract public function get($key); + + /** + * Deletes a value from the shared memory storage. + * + * @param string $key Name of key to delete. + * + * @return bool TRUE on success, FALSE on failure. + */ + abstract public function delete($key); + + /** + * Increases a value from the shared memory storage. + * + * Increases a value from the shared memory storage. Unlike a plain + * set($key, get($key)+$step) combination, this function also implicitly + * performs locking. + * + * @param string $key Name of key to increase. + * @param int $step Value to increase the key by. + * + * @return int The new value. + */ + abstract public function inc($key, $step = 1); + + /** + * Decreases a value from the shared memory storage. + * + * Decreases a value from the shared memory storage. Unlike a plain + * set($key, get($key)-$step) combination, this function also implicitly + * performs locking. + * + * @param string $key Name of key to decrease. + * @param int $step Value to decrease the key by. + * + * @return int The new value. + */ + abstract public function dec($key, $step = 1); + + /** + * Sets a new value if a key has a certain value. + * + * Sets a new value if a key has a certain value. This function only works + * when $old and $new are longs. + * + * @param string $key Key of the value to compare and set. + * @param int $old The value to compare the key against. + * @param int $new The value to set the key to. + * + * @return bool TRUE on success, FALSE on failure. + */ + abstract public function cas($key, $old, $new); + + /** + * Clears the persistent storage. + * + * Clears the persistent storage, i.e. removes all keys. Locks are left + * intact. + * + * @return void + */ + abstract public function clear(); + + /** + * Retrieve an external iterator + * + * Returns an external iterator. + * + * @param string|null $filter A PCRE regular expression. + * Only matching keys will be iterated over. + * Setting this to NULL matches all keys of this instance. + * @param bool $keysOnly Whether to return only the keys, + * or return both the keys and values. + * + * @return \Traversable An array with all matching keys as array keys, + * and values as array values. If $keysOnly is TRUE, the array keys are + * numeric, and the array values are key names. + */ + abstract public function getIterator($filter = null, $keysOnly = false); +} + +SHM::registerAdapter('\\' . __NAMESPACE__ . '\SHM\Adapter\Placebo'); +SHM::registerAdapter('\\' . __NAMESPACE__ . '\SHM\Adapter\Wincache'); +SHM::registerAdapter('\\' . __NAMESPACE__ . '\SHM\Adapter\APC'); diff --git a/system/autoload/PEAR2/Cache/SHM/Adapter/APC.php b/system/autoload/PEAR2/Cache/SHM/Adapter/APC.php new file mode 100644 index 0000000..a1b6ee7 --- /dev/null +++ b/system/autoload/PEAR2/Cache/SHM/Adapter/APC.php @@ -0,0 +1,406 @@ + + * @copyright 2011 Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 0.1.3 + * @link http://pear2.php.net/PEAR2_Cache_SHM + */ +/** + * The namespace declaration. + */ +namespace PEAR2\Cache\SHM\Adapter; + +/** + * Throws exceptions from this namespace, and extends from this class. + */ +use PEAR2\Cache\SHM; + +/** + * {@link APC::getIterator()} returns this object. + */ +use ArrayObject; + +/** + * Shared memory adapter for the APC extension. + * + * @category Caching + * @package PEAR2_Cache_SHM + * @author Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Cache_SHM + */ +class APC extends SHM +{ + /** + * @var string ID of the current storage. + */ + protected $persistentId; + + /** + * List of persistent IDs. + * + * A list of persistent IDs within the current request (as keys) with an int + * (as a value) specifying the number of instances in the current request. + * Used as an attempt to ensure implicit lock releases even on errors in the + * critical sections, since APC doesn't have an actual locking function. + * @var array + */ + protected static $requestInstances = array(); + + /** + * @var array Array of lock names (as values) for each persistent ID (as + * key) obtained during the current request. + */ + protected static $locksBackup = array(); + + /** + * Creates a new shared memory storage. + * + * Estabilishes a separate persistent storage. + * + * @param string $persistentId The ID for the storage. The storage will be + * reused if it exists, or created if it doesn't exist. Data and locks + * are namespaced by this ID. + */ + public function __construct($persistentId) + { + $this->persistentId = __CLASS__ . ' ' . $persistentId; + if (isset(static::$requestInstances[$this->persistentId])) { + static::$requestInstances[$this->persistentId]++; + } else { + static::$requestInstances[$this->persistentId] = 1; + static::$locksBackup[$this->persistentId] = array(); + } + register_shutdown_function( + get_called_class() . '::releaseLocks', + $this->persistentId, + true + ); + } + + /** + * Checks if the adapter meets its requirements. + * + * @return bool TRUE on success, FALSE on failure. + */ + public static function isMeetingRequirements() + { + return extension_loaded('apc') + && version_compare(phpversion('apc'), '3.0.13', '>=') + && ini_get('apc.enabled') + && ('cli' !== PHP_SAPI || ini_get('apc.enable_cli')); + } + + /** + * Releases all locks in a storage. + * + * This function is not meant to be used directly. It is implicitly called + * by the the destructor and as a shutdown function when the request ends. + * One of these calls ends up releasing any unreleased locks obtained + * during the request. A lock is also implicitly released as soon as there + * are no objects left in the current request using the same persistent ID. + * + * @param string $internalPersistentId The internal persistent ID, the locks + * of which are being released. + * @param bool $isAtShutdown Whether the function was executed at + * shutdown. + * + * @return void + * @internal + */ + public static function releaseLocks($internalPersistentId, $isAtShutdown) + { + $hasInstances = 0 !== static::$requestInstances[$internalPersistentId]; + if ($isAtShutdown === $hasInstances) { + foreach (static::$locksBackup[$internalPersistentId] as $key) { + apc_delete($internalPersistentId . 'l ' . $key); + } + } + } + + /** + * Releases any locks obtained by this instance as soon as there are no more + * references to the object's persistent ID. + */ + public function __destruct() + { + static::$requestInstances[$this->persistentId]--; + static::releaseLocks($this->persistentId, false); + } + + + /** + * Obtains a named lock. + * + * @param string $key Name of the key to obtain. Note that $key may + * repeat for each distinct $persistentId. + * @param double $timeout If the lock can't be immediatly obtained, the + * script will block for at most the specified amount of seconds. + * Setting this to 0 makes lock obtaining non blocking, and setting it + * to NULL makes it block without a time limit. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function lock($key, $timeout = null) + { + $lock = $this->persistentId . 'l ' . $key; + $hasTimeout = $timeout !== null; + $start = microtime(true); + while (!apc_add($lock, 1)) { + if ($hasTimeout && (microtime(true) - $start) > $timeout) { + return false; + } + } + static::$locksBackup[$this->persistentId] = $key; + return true; + } + + /** + * Releases a named lock. + * + * @param string $key Name of the key to release. Note that $key may + * repeat for each distinct $persistentId. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function unlock($key) + { + $lock = $this->persistentId . 'l ' . $key; + $success = apc_delete($lock); + if ($success) { + unset(static::$locksBackup[$this->persistentId][array_search( + $key, + static::$locksBackup[$this->persistentId], + true + )]); + return true; + } + return false; + } + + /** + * Checks if a specified key is in the storage. + * + * @param string $key Name of key to check. + * + * @return bool TRUE if the key is in the storage, FALSE otherwise. + */ + public function exists($key) + { + return apc_exists($this->persistentId . 'd ' . $key); + } + + /** + * Adds a value to the shared memory storage. + * + * Adds a value to the storage if it doesn't exist, or fails if it does. + * + * @param string $key Name of key to associate the value with. + * @param mixed $value Value for the specified key. + * @param int $ttl Seconds to store the value. If set to 0 indicates no + * time limit. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function add($key, $value, $ttl = 0) + { + return apc_add($this->persistentId . 'd ' . $key, $value, $ttl); + } + + /** + * Sets a value in the shared memory storage. + * + * Adds a value to the storage if it doesn't exist, overwrites it otherwise. + * + * @param string $key Name of key to associate the value with. + * @param mixed $value Value for the specified key. + * @param int $ttl Seconds to store the value. If set to 0 indicates no + * time limit. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function set($key, $value, $ttl = 0) + { + return apc_store($this->persistentId . 'd ' . $key, $value, $ttl); + } + + /** + * Gets a value from the shared memory storage. + * + * Gets the current value, or throws an exception if it's not stored. + * + * @param string $key Name of key to get the value of. + * + * @return mixed The current value of the specified key. + */ + public function get($key) + { + $fullKey = $this->persistentId . 'd ' . $key; + if (apc_exists($fullKey)) { + $value = apc_fetch($fullKey, $success); + if (!$success) { + throw new SHM\InvalidArgumentException( + 'Unable to fetch key. ' . + 'Key has either just now expired or (if no TTL was set) ' . + 'is possibly in a race condition with another request.', + 100 + ); + } + return $value; + } + throw new SHM\InvalidArgumentException('No such key in cache', 101); + } + + /** + * Deletes a value from the shared memory storage. + * + * @param string $key Name of key to delete. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function delete($key) + { + return apc_delete($this->persistentId . 'd ' . $key); + } + + /** + * Increases a value from the shared memory storage. + * + * Increases a value from the shared memory storage. Unlike a plain + * set($key, get($key)+$step) combination, this function also implicitly + * performs locking. + * + * @param string $key Name of key to increase. + * @param int $step Value to increase the key by. + * + * @return int The new value. + */ + public function inc($key, $step = 1) + { + $newValue = apc_inc( + $this->persistentId . 'd ' . $key, + (int) $step, + $success + ); + if (!$success) { + throw new SHM\InvalidArgumentException( + 'Unable to increase the value. Are you sure the value is int?', + 102 + ); + } + return $newValue; + } + + /** + * Decreases a value from the shared memory storage. + * + * Decreases a value from the shared memory storage. Unlike a plain + * set($key, get($key)-$step) combination, this function also implicitly + * performs locking. + * + * @param string $key Name of key to decrease. + * @param int $step Value to decrease the key by. + * + * @return int The new value. + */ + public function dec($key, $step = 1) + { + $newValue = apc_dec( + $this->persistentId . 'd ' . $key, + (int) $step, + $success + ); + if (!$success) { + throw new SHM\InvalidArgumentException( + 'Unable to decrease the value. Are you sure the value is int?', + 103 + ); + } + return $newValue; + } + + /** + * Sets a new value if a key has a certain value. + * + * Sets a new value if a key has a certain value. This function only works + * when $old and $new are longs. + * + * @param string $key Key of the value to compare and set. + * @param int $old The value to compare the key against. + * @param int $new The value to set the key to. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function cas($key, $old, $new) + { + return apc_cas($this->persistentId . 'd ' . $key, $old, $new); + } + + /** + * Clears the persistent storage. + * + * Clears the persistent storage, i.e. removes all keys. Locks are left + * intact. + * + * @return void + */ + public function clear() + { + foreach (new APCIterator( + 'user', + '/^' . preg_quote($this->persistentId, '/') . 'd /', + APC_ITER_KEY, + 100, + APC_LIST_ACTIVE + ) as $key) { + apc_delete($key); + } + } + + /** + * Retrieve an external iterator + * + * Returns an external iterator. + * + * @param string|null $filter A PCRE regular expression. + * Only matching keys will be iterated over. + * Setting this to NULL matches all keys of this instance. + * @param bool $keysOnly Whether to return only the keys, + * or return both the keys and values. + * + * @return ArrayObject An array with all matching keys as array keys, + * and values as array values. If $keysOnly is TRUE, the array keys are + * numeric, and the array values are key names. + */ + public function getIterator($filter = null, $keysOnly = false) + { + $result = array(); + foreach (new APCIterator( + 'user', + '/^' . preg_quote($this->persistentId, '/') . 'd /', + APC_ITER_KEY, + 100, + APC_LIST_ACTIVE + ) as $key) { + $localKey = strstr($key, $this->persistentId . 'd '); + if (null === $filter || preg_match($filter, $localKey)) { + if ($keysOnly) { + $result[] = $localKey; + } else { + $result[$localKey] = apc_fetch($key); + } + } + } + return new ArrayObject($result); + } +} diff --git a/system/autoload/PEAR2/Cache/SHM/Adapter/Placebo.php b/system/autoload/PEAR2/Cache/SHM/Adapter/Placebo.php new file mode 100644 index 0000000..bbce22f --- /dev/null +++ b/system/autoload/PEAR2/Cache/SHM/Adapter/Placebo.php @@ -0,0 +1,358 @@ + + * @copyright 2011 Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 0.1.3 + * @link http://pear2.php.net/PEAR2_Cache_SHM + */ +/** + * The namespace declaration. + */ +namespace PEAR2\Cache\SHM\Adapter; + +/** + * Throws exceptions from this namespace, and extends from this class. + */ +use PEAR2\Cache\SHM; + +/** + * {@link Placebo::getIterator()} returns this object. + */ +use ArrayObject; + +/** + * This adapter is not truly persistent. It is intended to emulate persistency + * in non persistent environments, so that upper level applications can use a + * single code path for persistent and non persistent code. + * + * @category Caching + * @package PEAR2_Cache_SHM + * @author Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Cache_SHM + */ +class Placebo extends SHM +{ + /** + * @var string ID of the current storage. + */ + protected $persistentId; + + /** + * List of persistent IDs. + * + * A list of persistent IDs within the current request (as keys) with an int + * (as a value) specifying the number of instances in the current request. + * Used as an attempt to ensure implicit lock releases on destruction. + * @var array + */ + protected static $requestInstances = array(); + + /** + * @var array Array of lock names (as values) for each persistent ID (as + * key) obtained during the current request. + */ + protected static $locksBackup = array(); + + /** + * The data storage. + * + * Each persistent ID is a key, and the value is an array. + * Each such array has data keys as its keys, and an array as a value. + * Each such array has as its elements the value, the timeout and the time + * the data was set. + * @var array + */ + protected static $data = array(); + + /** + * Creates a new shared memory storage. + * + * Estabilishes a separate persistent storage. + * + * @param string $persistentId The ID for the storage. The storage will be + * reused if it exists, or created if it doesn't exist. Data and locks + * are namespaced by this ID. + */ + public function __construct($persistentId) + { + if (isset(static::$requestInstances[$persistentId])) { + ++static::$requestInstances[$persistentId]; + } else { + static::$requestInstances[$persistentId] = 1; + static::$locksBackup[$persistentId] = array(); + static::$data[$persistentId] = array(); + } + $this->persistentId = $persistentId; + } + + /** + * Releases any unreleased locks. + */ + public function __destruct() + { + if (0 === --static::$requestInstances[$this->persistentId]) { + static::$locksBackup[$this->persistentId] = array(); + } + } + + /** + * Checks if the adapter meets its requirements. + * + * @return bool TRUE on success, FALSE on failure. + */ + public static function isMeetingRequirements() + { + return 'cli' === PHP_SAPI; + } + + /** + * Pretends to obtain a lock. + * + * @param string $key Ignored. + * @param double $timeout Ignored. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function lock($key, $timeout = null) + { + $key = (string) $key; + if (in_array($key, static::$locksBackup[$this->persistentId], true)) { + return false; + } + static::$locksBackup[$this->persistentId][] = $key; + return true; + } + + /** + * Pretends to release a lock. + * + * @param string $key Ignored + * + * @return bool TRUE on success, FALSE on failure. + */ + public function unlock($key) + { + $key = (string) $key; + if (!in_array($key, static::$locksBackup[$this->persistentId], true)) { + return false; + } + unset(static::$locksBackup[$this->persistentId][array_search( + $key, + static::$locksBackup[$this->persistentId], + true + )]); + return true; + } + + /** + * Checks if a specified key is in the storage. + * + * @param string $key Name of key to check. + * + * @return bool TRUE if the key is in the storage, FALSE otherwise. + */ + public function exists($key) + { + return array_key_exists($key, static::$data[$this->persistentId]); + } + + /** + * Adds a value to the shared memory storage. + * + * Adds a value to the storage if it doesn't exist, or fails if it does. + * + * @param string $key Name of key to associate the value with. + * @param mixed $value Value for the specified key. + * @param int $ttl Because "true" adapters purge the cache at the next + * request, this setting is ignored. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function add($key, $value, $ttl = 0) + { + if ($this->exists($key)) { + return false; + } + return $this->set($key, $value, $ttl); + } + + /** + * Sets a value in the shared memory storage. + * + * Adds a value to the storage if it doesn't exist, overwrites it otherwise. + * + * @param string $key Name of key to associate the value with. + * @param mixed $value Value for the specified key. + * @param int $ttl Because "true" adapters purge the cache at the next + * request, this setting is ignored. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function set($key, $value, $ttl = 0) + { + static::$data[$this->persistentId][$key] = $value; + return true; + } + + /** + * Gets a value from the shared memory storage. + * + * Gets the current value, or throws an exception if it's not stored. + * + * @param string $key Name of key to get the value of. + * + * @return mixed The current value of the specified key. + */ + public function get($key) + { + if ($this->exists($key)) { + return static::$data[$this->persistentId][$key]; + } + throw new SHM\InvalidArgumentException( + 'Unable to fetch key. No such key.', + 200 + ); + } + + /** + * Deletes a value from the shared memory storage. + * + * @param string $key Name of key to delete. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function delete($key) + { + if ($this->exists($key)) { + unset(static::$data[$this->persistentId][$key]); + return true; + } + return false; + } + + /** + * Increases a value from the shared memory storage. + * + * Increases a value from the shared memory storage. Unlike a plain + * set($key, get($key)+$step) combination, this function also implicitly + * performs locking. + * + * @param string $key Name of key to increase. + * @param int $step Value to increase the key by. + * + * @return int The new value. + */ + public function inc($key, $step = 1) + { + if (!$this->exists($key) || !is_int($value = $this->get($key)) + || !$this->set($key, $value + (int) $step) + ) { + throw new SHM\InvalidArgumentException( + 'Unable to increase the value. Are you sure the value is int?', + 201 + ); + } + return $this->get($key); + } + + /** + * Decreases a value from the shared memory storage. + * + * Decreases a value from the shared memory storage. Unlike a plain + * set($key, get($key)-$step) combination, this function also implicitly + * performs locking. + * + * @param string $key Name of key to decrease. + * @param int $step Value to decrease the key by. + * + * @return int The new value. + */ + public function dec($key, $step = 1) + { + if (!$this->exists($key) || !is_int($value = $this->get($key)) + || !$this->set($key, $value - (int) $step) + ) { + throw new SHM\InvalidArgumentException( + 'Unable to increase the value. Are you sure the value is int?', + 202 + ); + } + return $this->get($key); + } + + /** + * Sets a new value if a key has a certain value. + * + * Sets a new value if a key has a certain value. This function only works + * when $old and $new are longs. + * + * @param string $key Key of the value to compare and set. + * @param int $old The value to compare the key against. + * @param int $new The value to set the key to. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function cas($key, $old, $new) + { + return $this->exists($key) && ($this->get($key) === $old) + && is_int($new) && $this->set($key, $new); + } + + /** + * Clears the persistent storage. + * + * Clears the persistent storage, i.e. removes all keys. Locks are left + * intact. + * + * @return void + */ + public function clear() + { + static::$data[$this->persistentId] = array(); + } + + /** + * Retrieve an external iterator + * + * Returns an external iterator. + * + * @param string|null $filter A PCRE regular expression. + * Only matching keys will be iterated over. + * Setting this to NULL matches all keys of this instance. + * @param bool $keysOnly Whether to return only the keys, + * or return both the keys and values. + * + * @return ArrayObject An array with all matching keys as array keys, + * and values as array values. If $keysOnly is TRUE, the array keys are + * numeric, and the array values are key names. + */ + public function getIterator($filter = null, $keysOnly = false) + { + if (null === $filter) { + return new ArrayObject( + $keysOnly + ? array_keys(static::$data[$this->persistentId]) + : static::$data[$this->persistentId] + ); + } + + $result = array(); + foreach (static::$data[$this->persistentId] as $key => $value) { + if (preg_match($filter, $key)) { + $result[$key] = $value; + } + } + return new ArrayObject($keysOnly ? array_keys($result) : $result); + } +} diff --git a/system/autoload/PEAR2/Cache/SHM/Adapter/Wincache.php b/system/autoload/PEAR2/Cache/SHM/Adapter/Wincache.php new file mode 100644 index 0000000..5d2e726 --- /dev/null +++ b/system/autoload/PEAR2/Cache/SHM/Adapter/Wincache.php @@ -0,0 +1,383 @@ + + * @copyright 2011 Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 0.1.3 + * @link http://pear2.php.net/PEAR2_Cache_SHM + */ +/** + * The namespace declaration. + */ +namespace PEAR2\Cache\SHM\Adapter; + +/** + * Throws exceptions from this namespace, and extends from this class. + */ +use PEAR2\Cache\SHM; + +/** + * {@link Wincache::getIterator()} returns this object. + */ +use ArrayObject; + +/** + * Shared memory adapter for the WinCache extension. + * + * @category Caching + * @package PEAR2_Cache_SHM + * @author Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Cache_SHM + */ +class Wincache extends SHM +{ + /** + * @var string ID of the current storage. + */ + protected $persistentId; + + /** + * List of persistent IDs. + * + * A list of persistent IDs within the current request (as keys) with an int + * (as a value) specifying the number of instances in the current request. + * Used as an attempt to ensure implicit lock releases on destruction. + * @var array + */ + protected static $requestInstances = array(); + + /** + * @var array Array of lock names obtained during the current request. + */ + protected static $locksBackup = array(); + + /** + * Creates a new shared memory storage. + * + * Estabilishes a separate persistent storage. + * + * @param string $persistentId The ID for the storage. The storage will be + * reused if it exists, or created if it doesn't exist. Data and locks + * are namespaced by this ID. + */ + public function __construct($persistentId) + { + $this->persistentId + = static::encodeLockName(__CLASS__ . ' ' . $persistentId) . ' '; + if (isset(static::$requestInstances[$this->persistentId])) { + static::$requestInstances[$this->persistentId]++; + } else { + static::$requestInstances[$this->persistentId] = 1; + static::$locksBackup[$this->persistentId] = array(); + } + } + + /** + * Encodes a lock name + * + * Encodes a lock name, so that it can be properly obtained. The scheme used + * is a subset of URL encoding, with only the "%" and "\" characters being + * escaped. The encoding itself is necessary, since lock names can't contain + * the "\" character. + * + * @param string $name The lock name to encode. + * + * @return string The encoded name. + * @link http://msdn.microsoft.com/en-us/library/ms682411(VS.85).aspx + */ + protected static function encodeLockName($name) + { + return str_replace(array('%', '\\'), array('%25', '%5C'), $name); + } + + /** + * Checks if the adapter meets its requirements. + * + * @return bool TRUE on success, FALSE on failure. + */ + public static function isMeetingRequirements() + { + return extension_loaded('wincache') + && version_compare(phpversion('wincache'), '1.1.0', '>=') + && ini_get('wincache.ucenabled') + && ('cli' !== PHP_SAPI || ini_get('wincache.enablecli')); + } + + /** + * Releases any locks obtained by this instance as soon as there are no more + * references to the object's persistent ID. + */ + public function __destruct() + { + if (0 === --static::$requestInstances[$this->persistentId]) { + foreach (static::$locksBackup[$this->persistentId] as $key) { + wincache_unlock( + $this->persistentId . static::encodeLockName($key) + ); + } + } + } + + + /** + * Obtains a named lock. + * + * @param string $key Name of the key to obtain. Note that $key may + * repeat for each distinct $persistentId. + * @param double $timeout Ignored with WinCache. Script will always block if + * the lock can't be immediatly obtained. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function lock($key, $timeout = null) + { + $result = wincache_lock( + $this->persistentId . static::encodeLockName($key) + ); + if ($result) { + static::$locksBackup[$this->persistentId] = $key; + } + return $result; + } + + /** + * Releases a named lock. + * + * @param string $key Name of the key to release. Note that $key may + * repeat for each distinct $persistentId. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function unlock($key) + { + $result = wincache_unlock( + $this->persistentId . static::encodeLockName($key) + ); + if ($result) { + unset(static::$locksBackup[$this->persistentId][array_search( + $key, + static::$locksBackup[$this->persistentId], + true + )]); + } + return $result; + } + + /** + * Checks if a specified key is in the storage. + * + * @param string $key Name of key to check. + * + * @return bool TRUE if the key is in the storage, FALSE otherwise. + */ + public function exists($key) + { + return wincache_ucache_exists($this->persistentId . $key); + } + + /** + * Adds a value to the shared memory storage. + * + * Sets a value to the storage if it doesn't exist, or fails if it does. + * + * @param string $key Name of key to associate the value with. + * @param mixed $value Value for the specified key. + * @param int $ttl Seconds to store the value. If set to 0 indicates no + * time limit. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function add($key, $value, $ttl = 0) + { + return wincache_ucache_add($this->persistentId . $key, $value, $ttl); + } + + /** + * Sets a value in the shared memory storage. + * + * Adds a value to the storage if it doesn't exist, overwrites it otherwise. + * + * @param string $key Name of key to associate the value with. + * @param mixed $value Value for the specified key. + * @param int $ttl Seconds to store the value. If set to 0 indicates no + * time limit. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function set($key, $value, $ttl = 0) + { + return wincache_ucache_set($this->persistentId . $key, $value, $ttl); + } + + /** + * Gets a value from the shared memory storage. + * + * Gets the current value, or throws an exception if it's not stored. + * + * @param string $key Name of key to get the value of. + * + * @return mixed The current value of the specified key. + */ + public function get($key) + { + $value = wincache_ucache_get($this->persistentId . $key, $success); + if (!$success) { + throw new SHM\InvalidArgumentException( + 'Unable to fetch key. No such key, or key has expired.', + 300 + ); + } + return $value; + } + + /** + * Deletes a value from the shared memory storage. + * + * @param string $key Name of key to delete. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function delete($key) + { + return wincache_ucache_delete($this->persistentId . $key); + } + + /** + * Increases a value from the shared memory storage. + * + * Increases a value from the shared memory storage. Unlike a plain + * set($key, get($key)+$step) combination, this function also implicitly + * performs locking. + * + * @param string $key Name of key to increase. + * @param int $step Value to increase the key by. + * + * @return int The new value. + */ + public function inc($key, $step = 1) + { + $newValue = wincache_ucache_inc( + $this->persistentId . $key, + (int) $step, + $success + ); + if (!$success) { + throw new SHM\InvalidArgumentException( + 'Unable to increase the value. Are you sure the value is int?', + 301 + ); + } + return $newValue; + } + + /** + * Decreases a value from the shared memory storage. + * + * Decreases a value from the shared memory storage. Unlike a plain + * set($key, get($key)-$step) combination, this function also implicitly + * performs locking. + * + * @param string $key Name of key to decrease. + * @param int $step Value to decrease the key by. + * + * @return int The new value. + */ + public function dec($key, $step = 1) + { + $newValue = wincache_ucache_dec( + $this->persistentId . $key, + (int) $step, + $success + ); + if (!$success) { + throw new SHM\InvalidArgumentException( + 'Unable to decrease the value. Are you sure the value is int?', + 302 + ); + } + return $newValue; + } + + /** + * Sets a new value if a key has a certain value. + * + * Sets a new value if a key has a certain value. This function only works + * when $old and $new are longs. + * + * @param string $key Key of the value to compare and set. + * @param int $old The value to compare the key against. + * @param int $new The value to set the key to. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function cas($key, $old, $new) + { + return wincache_ucache_cas($this->persistentId . $key, $old, $new); + } + + /** + * Clears the persistent storage. + * + * Clears the persistent storage, i.e. removes all keys. Locks are left + * intact. + * + * @return void + */ + public function clear() + { + $info = wincache_ucache_info(); + foreach ($info['ucache_entries'] as $entry) { + if (!$entry['is_session'] + && 0 === strpos($entry['key_name'], $this->persistentId) + ) { + wincache_ucache_delete($entry['key_name']); + } + } + } + + /** + * Retrieve an external iterator + * + * Returns an external iterator. + * + * @param string|null $filter A PCRE regular expression. + * Only matching keys will be iterated over. + * Setting this to NULL matches all keys of this instance. + * @param bool $keysOnly Whether to return only the keys, + * or return both the keys and values. + * + * @return ArrayObject An array with all matching keys as array keys, + * and values as array values. If $keysOnly is TRUE, the array keys are + * numeric, and the array values are key names. + */ + public function getIterator($filter = null, $keysOnly = false) + { + $info = wincache_ucache_info(); + $result = array(); + foreach ($info['ucache_entries'] as $entry) { + if (!$entry['is_session'] + && 0 === strpos($entry['key_name'], $this->persistentId) + ) { + $localKey = strstr($entry['key_name'], $this->persistentId); + if (null === $filter || preg_match($filter, $localKey)) { + if ($keysOnly) { + $result[] = $localKey; + } else { + $result[$localKey] = apc_fetch($localKey); + } + } + } + } + return new ArrayObject($result); + } +} diff --git a/system/autoload/PEAR2/Cache/SHM/Exception.php b/system/autoload/PEAR2/Cache/SHM/Exception.php new file mode 100644 index 0000000..5984729 --- /dev/null +++ b/system/autoload/PEAR2/Cache/SHM/Exception.php @@ -0,0 +1,34 @@ + + * @copyright 2011 Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 0.1.3 + * @link http://pear2.php.net/PEAR2_Cache_SHM + */ +/** + * The namespace declaration. + */ +namespace PEAR2\Cache\SHM; + +/** + * Generic exception class of this package. + * + * @category Caching + * @package PEAR2_Cache_SHM + * @author Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Cache_SHM + */ +interface Exception +{ +} diff --git a/system/autoload/PEAR2/Cache/SHM/InvalidArgumentException.php b/system/autoload/PEAR2/Cache/SHM/InvalidArgumentException.php new file mode 100644 index 0000000..817f2ab --- /dev/null +++ b/system/autoload/PEAR2/Cache/SHM/InvalidArgumentException.php @@ -0,0 +1,35 @@ + + * @copyright 2011 Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 0.1.3 + * @link http://pear2.php.net/PEAR2_Cache_SHM + */ +/** + * The namespace declaration. + */ +namespace PEAR2\Cache\SHM; + +/** + * Exception thrown when there's something wrong with an argument. + * + * @category Caching + * @package PEAR2_Cache_SHM + * @author Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Cache_SHM + */ +class InvalidArgumentException extends \InvalidArgumentException + implements Exception +{ +} diff --git a/system/autoload/PEAR2/Console/Color.php b/system/autoload/PEAR2/Console/Color.php new file mode 100644 index 0000000..8f0cbf5 --- /dev/null +++ b/system/autoload/PEAR2/Console/Color.php @@ -0,0 +1,369 @@ + + * @author Ivo Nascimento + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 1.0.0 + * @link http://pear.php.net/package/Console_Color + */ +namespace PEAR2\Console; + +use PEAR2\Console\Color\Backgrounds; +use PEAR2\Console\Color\Flags; +use PEAR2\Console\Color\Fonts; +use PEAR2\Console\Color\Styles; +use PEAR2\Console\Color\UnexpectedValueException; +use ReflectionClass; + +/** + * Main class for Console_Color. + * + * @category Console + * @package Console_Color + * @author Ivo Nascimento + * @author Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Console_Color + */ +class Color +{ + /** + * @var array List of valid font colors. + * Filled by {@link fillValidators()}. + */ + protected static $validFonts = array(); + + /** + * @var array List of valid background colors. + * Filled by {@link fillValidators()}. + */ + protected static $validBackgorunds = array(); + + /** + * @var string Name of a class that is used to resolve flags to codes. + */ + protected static $flagsResolver = ''; + + /** + * @var string Name of a class that is used to resolve styles to codes. + */ + protected static $stylesResolver = ''; + + /** + * @var int Flags to set. + */ + protected $flags = 0; + + /** + * @var int|null The code for the currently specified font color. + */ + protected $font = null; + + /** + * @var int|null The code for the currently specified background color. + */ + protected $backgorund = null; + + /** + * @var bool[] Array with the status of each style. + */ + protected $styles = array(); + + /** + * @var string|null The string to write to console to get the specified + * styling. NULL when the string needs to be regenerated. + */ + protected $sequence = null; + + /** + * Fills the list of valid fonts and backgrounds. + * + * Classes extending this one that wish to add additional valid colors, + * flags or styles should call this method in their own constructor BEFORE + * calling the parent constructor. + * + * @param string $fonts Name of class, the constants of which are + * valid font colors. + * @param string $backgrounds Name of class, the constants of which are + * valid background colors. + * @param string $flags Name of class that resolves flags to codes. + * Must inheirt from {@link Flags}. Constants of this + * class are considered the valid flags, and the coresponding codes must + * be overriden at the static $flagCodes property. + * @param string $styles Name of class that resolves styles to codes. + * Must inherit from {@link Styles}. Constants of this class are + * considered the valid styles, and the corresponding off/on codes must + * be overriden at the static $styleCodes property. + * + * @return void + */ + protected static function fillVlidators( + $fonts, + $backgrounds, + $flags, + $styles + ) { + if (empty(static::$validFonts)) { + $fonts = new ReflectionClass($fonts); + static::$validFonts = array_values( + array_unique($fonts->getConstants(), SORT_REGULAR) + ); + } + + if (empty(static::$validBackgorunds)) { + $bgs = new ReflectionClass($backgrounds); + static::$validBackgorunds = array_values( + array_unique($bgs->getConstants(), SORT_REGULAR) + ); + } + + if ('' === static::$flagsResolver) { + $base = __CLASS__ . '\Flags'; + if ($base === $flags || is_subclass_of($flags, $base)) { + static::$flagsResolver = $flags; + } + } + + if ('' === static::$stylesResolver) { + $base = __CLASS__ . '\Styles'; + if ($base === $styles || is_subclass_of($styles, $base)) { + static::$stylesResolver = $styles; + } + } + } + + /** + * Creates a new color. + * + * Note that leaving all arguments with their default values (and not + * applying styles) would result in a sequence that resets all settings to + * the console's defaults. + * + * @param int|null $font Initial font color. + * @param int|null $background Initial backgorund color. + * @param int $flags Initial flags. + * + * @see setFlags() + * @see setStyles() + * @see __toString() + */ + public function __construct( + $font = Fonts::KEEP, + $background = Backgrounds::KEEP, + $flags = Flags::NONE + ) { + static::fillVlidators( + __CLASS__ . '\Fonts', + __CLASS__ . '\Backgrounds', + __CLASS__ . '\Flags', + __CLASS__ . '\Styles' + ); + $this->setFont($font); + $this->setBackground($background); + $this->setFlags($flags); + } + + /** + * Gets the font color. + * + * @return int|null $color The font color. + */ + public function getFont() + { + return $this->font; + } + + /** + * Sets the font color. + * + * @param int|null $color The font color. + * + * @return $this + */ + public function setFont($color) + { + if (!in_array($color, static::$validFonts, true)) { + throw new UnexpectedValueException( + 'Invalid font supplied.', + UnexpectedValueException::CODE_FONT + ); + } + $this->font = $color; + + $this->sequence = null; + return $this; + } + + /** + * Gets the background color. + * + * @return int|null $color The background color. + */ + public function getBackground() + { + return $this->backgorund; + } + + /** + * Sets the background color. + * + * @param int|null $color The background color. + * + * @return $this + */ + public function setBackground($color) + { + if (!in_array($color, static::$validBackgorunds, true)) { + throw new UnexpectedValueException( + 'Invalid background supplied.', + UnexpectedValueException::CODE_BACKGROUND + ); + } + $this->backgorund = $color; + + $this->sequence = null; + return $this; + } + + /** + * Gets the flags. + * + * @return int The currently set flags. + */ + public function getFlags() + { + return $this->flags; + } + + /** + * Sets the flags. + * + * Sets the flags to apply in the sequence. Note that flags are applied + * before all other settings, in ascending order of the constant values. + * + * @param int $flags The new flags to set. Unknown flags will be ignored + * when forming the sequence, but will be visible with + * {@link getFlags()} non the less. + * + * @return $this + */ + public function setFlags($flags) + { + $this->flags = (int)$flags; + + $this->sequence = null; + return $this; + } + + /** + * Gets styles. + * + * @param int|null $style A single style to get the status of, + * or {@link Styles::ALL} to get all styles in an array. + * + * @return bool|null|bool[] A single style status, or + * an array of status if $style is {@link Styles::ALL}. + */ + public function getStyles($style = Styles::ALL) + { + if (Styles::ALL === $style) { + return $this->styles; + } + return isset($this->styles[$style]) ? $this->styles[$style] : null; + } + + /** + * Sets styles. + * + * Sets styles matched to a specified state. + * + * @param int|null $styles Bitmask of styles to set. You can also use the + * constant {@link Styles::ALL} (only) to set all known styles. + * Unknown styles will be ignored. + * @param bool|null $state The state to set the matched styles in. + * TRUE to enable them, + * FLASE to disable them, + * NULL to remove the setting for them (in effect using whatever the + * console had before the sequence was applied). + * + * @return $this + */ + public function setStyles($styles, $state) + { + $matchingStyles = call_user_func( + array(static::$stylesResolver, 'match'), + $styles + ); + if (null === $state) { + foreach ($matchingStyles as $style) { + unset($this->styles[$style]); + } + } else { + $state = (bool)$state; + foreach ($matchingStyles as $style) { + $this->styles[$style] = $state; + } + ksort($this->styles); + } + + $this->sequence = null; + return $this; + } + + /** + * Get the console escaping sequence. + * + * This is a magic PHP method that will be called when you use the object in + * a string context or otherwise explicitly cast it to a string. + * + * It generates the escape sequence and returns it. + * For the sake of performance, the escape sequence is cached, and is only + * regenerated when a setter has been previously called. + * + * @return string The string to write to console to get the specified + * styling. + */ + public function __toString() + { + if (null === $this->sequence) { + $seq = "\033["; + + $flags = implode( + ';', + call_user_func( + array(static::$flagsResolver, 'getCodes'), + $this->flags + ) + ); + if ('' !== $flags) { + $seq .= $flags . ';'; + } + + if (Fonts::KEEP !== $this->font) { + $seq .= "{$this->font};"; + } + if (Backgrounds::KEEP !== $this->backgorund) { + $seq .= "{$this->backgorund};"; + } + + foreach ($this->styles as $style => $state) { + $seq .= call_user_func( + array(static::$stylesResolver, 'getCode'), + $style, + $state + ) . ';'; + } + + $this->sequence = rtrim($seq, ';') . 'm'; + } + + return $this->sequence; + } +} diff --git a/system/autoload/PEAR2/Console/Color/Backgrounds.php b/system/autoload/PEAR2/Console/Color/Backgrounds.php new file mode 100644 index 0000000..e183222 --- /dev/null +++ b/system/autoload/PEAR2/Console/Color/Backgrounds.php @@ -0,0 +1,136 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 1.0.0 + * @link http://pear2.php.net/PEAR2_Console_Color + */ +namespace PEAR2\Console\Color; + +/** + * This class has the possibles values to a Background Color. + * + * @category Console + * @package PEAR2_Console_Color + * @author Ivo Nascimento + * @copyright 2011 Ivo Nascimento + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Console_Color + */ +abstract class Backgrounds +{ + /** + * Used at {@link \PEAR2\Console\Color::setBackground()} to specify that + * the background color already in effect should be kept. + */ + const KEEP = null; + + /** + * Used at {@link \PEAR2\Console\Color::setBackground()} to set the + * background color to black/grey (implmementation defined). + */ + const BLACK = 40; + + /** + * Used at {@link \PEAR2\Console\Color::setBackground()} to set the + * background color to black/grey (implementation defined). + */ + const GREY = 40; + + /** + * Used at {@link \PEAR2\Console\Color::setBackground()} to set the + * background color to maroon/red (implementation defined). + */ + const MAROON = 41; + + /** + * Used at {@link \PEAR2\Console\Color::setBackground()} to set the + * background color to maroon/red (implementation defined). + */ + const RED = 41; + + /** + * Used at {@link \PEAR2\Console\Color::setBackground()} to set the + * background color to green/lime (implementation defined). + */ + const GREEN = 42; + + /** + * Used at {@link \PEAR2\Console\Color::setBackground()} to set the + * background color to green/lime (implementation defined). + */ + const LIME = 42; + + /** + * Used at {@link \PEAR2\Console\Color::setBackground()} to set the + * background color to brown/yellow (implementation defined). + */ + const BROWN = 43; + + /** + * Used at {@link \PEAR2\Console\Color::setBackground()} to set the + * background color to brown/yellow (implementation defined). + */ + const YELLOW = 43; + + /** + * Used at {@link \PEAR2\Console\Color::setBackground()} to set the + * background color to navy/blue (implementation defined). + */ + const NAVY = 44; + + /** + * Used at {@link \PEAR2\Console\Color::setBackground()} to set the + * background color to navy/blue (implementation defined). + */ + const BLUE = 44; + + /** + * Used at {@link \PEAR2\Console\Color::setBackground()} to set the + * background color to purple/magenta (implementation defined). + */ + const PURPLE = 45; + + /** + * Used at {@link \PEAR2\Console\Color::setBackground()} to set the + * background color to purple/magenta (implementation defined). + */ + const MAGENTA = 45; + + /** + * Used at {@link \PEAR2\Console\Color::setBackground()} to set the + * background color to teal/cyan (implementation defined). + */ + const TEAL = 46; + + /** + * Used at {@link \PEAR2\Console\Color::setBackground()} to set the + * background color to teal/cyan (implementation defined). + */ + const CYAN = 46; + + /** + * Used at {@link \PEAR2\Console\Color::setBackground()} to set the + * background color to silver/white (implementation defined). + */ + const SILVER = 47; + + /** + * Used at {@link \PEAR2\Console\Color::setBackground()} to set the + * background color to silver/white (implementation defined). + */ + const WHITE = 47; + + /** + * Used at {@link \PEAR2\Console\Color::setBackground()} to set the + * background color to whatever the default one is. + */ + const RESET = 49; +} diff --git a/system/autoload/PEAR2/Console/Color/Exception.php b/system/autoload/PEAR2/Console/Color/Exception.php new file mode 100644 index 0000000..926e8ce --- /dev/null +++ b/system/autoload/PEAR2/Console/Color/Exception.php @@ -0,0 +1,28 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 1.0.0 + * @link http://pear2.php.net/PEAR2_Console_Color + */ +namespace PEAR2\Console\Color; + +/** + * Exception class for PEAR2_Console_Color. + * + * @category Console + * @package PEAR2_Console_Color + * @author Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Console_Color + */ +interface Exception +{ +} diff --git a/system/autoload/PEAR2/Console/Color/Flags.php b/system/autoload/PEAR2/Console/Color/Flags.php new file mode 100644 index 0000000..3eb6545 --- /dev/null +++ b/system/autoload/PEAR2/Console/Color/Flags.php @@ -0,0 +1,88 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 1.0.0 + * @link http://pear2.php.net/PEAR2_Console_Color + */ +namespace PEAR2\Console\Color; + +use ReflectionClass; + +/** + * This class has the possibles flags to a color setting. + * + * @category Console + * @package PEAR2_Console_Color + * @author Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Console_Color + */ +abstract class Flags +{ + /** + * Used at {@link \PEAR2\Console\Color::setFlags()} to specify that no + * flags should be applied. + */ + const NONE = 0; + + /** + * Used at {@link \PEAR2\Console\Color::setFlags()} as part of a bitmask. + * If specified, resets all color and style information before applying + * everything else. + */ + const RESET = 1; + + /** + * Used at {@link \PEAR2\Console\Color::setFlags()} as part of a bitmask. + * If specified, inverses the font and background colors, before letting + * the remaining settings further modify things. + * If specified together with {@link self::RESET}, takes effect AFTER the + * reset. + */ + const INVERSE = 2; + + /** + * @var int[] Array with the flag as a key, and the corresponding code as a + * value. + */ + protected static $flagCodes = array( + self::RESET => 0, + self::INVERSE => 7 + ); + + /** + * Gets the codes for a flag set. + * + * @param int $flags The flags to get the codes for. + * + * @return int[] The codes for the flags specified, in ascending order, + * based on the flag constants' values. + */ + final public static function getCodes($flags) + { + if (self::NONE === $flags) { + return array(); + } + + $result = array(); + $flagsClass = new ReflectionClass(get_called_class()); + $validFlags = array_values( + array_unique($flagsClass->getConstants(), SORT_NUMERIC) + ); + foreach ($validFlags as $flag) { + if ($flags & $flag) { + $result[] = static::$flagCodes[$flag]; + } + } + return $result; + } +} diff --git a/system/autoload/PEAR2/Console/Color/Fonts.php b/system/autoload/PEAR2/Console/Color/Fonts.php new file mode 100644 index 0000000..8d9179c --- /dev/null +++ b/system/autoload/PEAR2/Console/Color/Fonts.php @@ -0,0 +1,136 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 1.0.0 + * @link http://pear2.php.net/PEAR2_Console_Color + */ +namespace PEAR2\Console\Color; + +/** + * This class has the possibles values to a Font Color. + * + * @category Console + * @package PEAR2_Console_Color + * @author Ivo Nascimento + * @copyright 2011 Ivo Nascimento + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Console_Color + */ +abstract class Fonts +{ + /** + * Used at {@link \PEAR2\Console\Color::setFont()} to specify that + * the font color already in effect should be kept. + */ + const KEEP = null; + + /** + * Used at {@link \PEAR2\Console\Color::setFont()} to set the + * font color to black/grey (implementation defined). + */ + const BLACK = 30; + + /** + * Used at {@link \PEAR2\Console\Color::setFont()} to set the + * font color to black/grey (implementation defined). + */ + const GREY = 30; + + /** + * Used at {@link \PEAR2\Console\Color::setFont()} to set the + * font color to maroon/red (implementation defined). + */ + const MAROON = 31; + + /** + * Used at {@link \PEAR2\Console\Color::setFont()} to set the + * font color to maroon/red (implementation defined). + */ + const RED = 31; + + /** + * Used at {@link \PEAR2\Console\Color::setFont()} to set the + * font color to green/lime (implementation defined). + */ + const LIME = 32; + + /** + * Used at {@link \PEAR2\Console\Color::setFont()} to set the + * font color to green/lime (implementation defined). + */ + const GREEN = 32; + + /** + * Used at {@link \PEAR2\Console\Color::setFont()} to set the + * font color to brown/yellow (implementation defined). + */ + const BROWN = 33; + + /** + * Used at {@link \PEAR2\Console\Color::setFont()} to set the + * font color to brown/yellow (implementation defined). + */ + const YELLOW = 33; + + /** + * Used at {@link \PEAR2\Console\Color::setFont()} to set the + * font color to navy/blue (implementation defined). + */ + const NAVY = 34; + + /** + * Used at {@link \PEAR2\Console\Color::setFont()} to set the + * font color to navy/blue (implementation defined). + */ + const BLUE = 34; + + /** + * Used at {@link \PEAR2\Console\Color::setFont()} to set the + * font color to purple/magenta (implementation defined). + */ + const PURPLE = 35; + + /** + * Used at {@link \PEAR2\Console\Color::setFont()} to set the + * font color to purple/magenta (implementation defined). + */ + const MAGENTA = 35; + + /** + * Used at {@link \PEAR2\Console\Color::setFont()} to set the + * font color to teal/cyan (implementation defined). + */ + const TEAL = 36; + + /** + * Used at {@link \PEAR2\Console\Color::setFont()} to set the + * font color to teal/cyan (implementation defined). + */ + const CYAN = 36; + + /** + * Used at {@link \PEAR2\Console\Color::setFont()} to set the + * font color to silver/white (implementation defined). + */ + const SILVER = 37; + + /** + * Used at {@link \PEAR2\Console\Color::setFont()} to set the + * font color to silver/white (implementation defined). + */ + const WHITE = 37; + + /** + * Used at {@link \PEAR2\Console\Color::setFont()} to set the + * font color to whatever the default one is. + */ + const RESET = 39; +} diff --git a/system/autoload/PEAR2/Console/Color/Styles.php b/system/autoload/PEAR2/Console/Color/Styles.php new file mode 100644 index 0000000..620f3c6 --- /dev/null +++ b/system/autoload/PEAR2/Console/Color/Styles.php @@ -0,0 +1,130 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 1.0.0 + * @link http://pear2.php.net/PEAR2_Console_Color + */ +namespace PEAR2\Console\Color; + +use ReflectionClass; + +/** + * This class has the possibles values to a Font Style. + * + * @category Console + * @package PEAR2_Console_Color + * @author Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Console_Color + */ +abstract class Styles +{ + /** + * Used in {@link \PEAR2\Console\Color::setStyles()} to match all styles. + */ + const ALL = null; + + /** + * Used in {@link \PEAR2\Console\Color::setStyles()} as part of a bitmask. + * If specified, matches the bold style. + * When this style is enabled, the font is bolder. + * With ANSICON, the font color becomes more intense (but not bolder). + */ + const BOLD = 1; + + /** + * Used in {@link \PEAR2\Console\Color::setStyles()} as part of a bitmask. + * If specified, matches the underline style. + * When this style is enabled, the font is underlined. + * With ANSICON, the background color becomes more intense + * (and the font is not underlined), same as {@link self::BLINK}. + */ + const UNDERLINE = 2; + + /** + * Used in {@link \PEAR2\Console\Color::setStyles()} as part of a bitmask. + * If specified, matches the blink style. + * When this style is enabled, the font color switches between its regular + * color and the background color at regular (implementation defined) + * intervals, creating the illusion of a blinking text. + * With ANSICON, the background color becomes more intense + * (and the font is not blinking), same as with {@link self::UNDERLINE}. + */ + const BLINK = 4; + + /** + * Used in {@link \PEAR2\Console\Color::setStyles()} as part of a bitmask. + * If specified, matches the concealed style. + * When this style is enabled, the font color becomes the background color, + * rendering the text invisible. This style is particularly useful for + * implementations where simply setting the same color and background color + * would not necesarily provide a fully invisibile text (e.g. ANSICON). + */ + const CONCEALED = 8; + + /** + * @var (int[])[] An array describing the codes for the styles. + * Each array key is the style's constant, and each value is an array + * where the first member is the disable code, and the second is the + * enable code. + */ + protected static $styleCodes = array( + self::BOLD => array(22, 1), + self::UNDERLINE => array(24, 4), + self::BLINK => array(25, 5), + self::CONCEALED => array(28, 8) + ); + + /** + * Get style constants. + * + * @param int|null $styles Bitmask of styles to match. + * You can also use {@link self::ALL} (only) to get all styles. + * + * @return int[] Matching style constants. + */ + final public static function match($styles) + { + $flagsClass = new ReflectionClass(get_called_class()); + $validStyles = array_values( + array_unique($flagsClass->getConstants(), SORT_NUMERIC) + ); + unset($validStyles[array_search(self::ALL, $validStyles, true)]); + + if (self::ALL === $styles) { + return $validStyles; + } + $styles = (int)$styles; + + $result = array(); + foreach ($validStyles as $flag) { + if ($styles & $flag) { + $result[] = $flag; + } + } + return $result; + } + + /** + * Gets the code for a style. + * + * @param int $style The style to get the code for. + * @param bool $state The state to get code for. + * TRUE for the enabled state codes, + * FALSE for the disabled state codes. + * + * @return int The code for the flag specified. + */ + final public static function getCode($style, $state) + { + return static::$styleCodes[$style][(int)(bool)$state]; + } +} diff --git a/system/autoload/PEAR2/Console/Color/UnexpectedValueException.php b/system/autoload/PEAR2/Console/Color/UnexpectedValueException.php new file mode 100644 index 0000000..6a2b51b --- /dev/null +++ b/system/autoload/PEAR2/Console/Color/UnexpectedValueException.php @@ -0,0 +1,40 @@ + + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 1.0.0 + * @link http://pear2.php.net/PEAR2_Console_Color + */ +namespace PEAR2\Console\Color; + +use UnexpectedValueException as U; + +/** + * Exception class for PEAR2_Console_Color. + * + * @category Console + * @package PEAR2_Console_Color + * @author Vasil Rangelov + * @copyright 2011 Ivo Nascimento + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Console_Color + */ +class UnexpectedValueException extends U implements Exception +{ + /** + * Used when an unexpected font value is supplied. + */ + const CODE_FONT = 1; + + /** + * Used when an unexpected background value is supplied. + */ + const CODE_BACKGROUND = 2; +} diff --git a/system/autoload/PEAR2/Console/CommandLine.php b/system/autoload/PEAR2/Console/CommandLine.php new file mode 100644 index 0000000..f2fc478 --- /dev/null +++ b/system/autoload/PEAR2/Console/CommandLine.php @@ -0,0 +1,1289 @@ + + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since Class available since release 0.1.0 + */ +namespace PEAR2\Console; + +/** + * Main class for parsing command line options and arguments. + * + * There are three ways to create parsers with this class: + * + * // direct usage + * $parser = new PEAR2\Console\CommandLine(); + * + * // with an xml definition file + * $parser = PEAR2\Console\CommandLine::fromXmlFile('path/to/file.xml'); + * + * // with an xml definition string + * $validXmlString = '..your xml string...'; + * $parser = PEAR2\Console\CommandLine::fromXmlString($validXmlString); + * + * + * @category Console + * @package PEAR2\Console\CommandLine + * @author David JEAN LOUIS + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since File available since release 0.1.0 + * @example docs/examples/ex1.php + * @example docs/examples/ex2.php + */ +class CommandLine +{ + // Public properties {{{ + + /** + * Error messages. + * + * @var array $errors Error messages + * @todo move this to PEAR2\Console\CommandLine\MessageProvider + */ + public static $errors = array( + 'option_bad_name' => 'option name must be a valid php variable name (got: {$name})', + 'argument_bad_name' => 'argument name must be a valid php variable name (got: {$name})', + 'argument_no_default' => 'only optional arguments can have a default value', + 'option_long_and_short_name_missing' => 'you must provide at least an option short name or long name for option "{$name}"', + 'option_bad_short_name' => 'option "{$name}" short name must be a dash followed by a letter (got: "{$short_name}")', + 'option_bad_long_name' => 'option "{$name}" long name must be 2 dashes followed by a word (got: "{$long_name}")', + 'option_unregistered_action' => 'unregistered action "{$action}" for option "{$name}".', + 'option_bad_action' => 'invalid action for option "{$name}".', + 'option_invalid_callback' => 'you must provide a valid callback for option "{$name}"', + 'action_class_does_not_exists' => 'action "{$name}" class "{$class}" not found, make sure that your class is available before calling PEAR2\Console\CommandLine::registerAction()', + 'invalid_xml_file' => 'XML definition file "{$file}" does not exists or is not readable', + 'invalid_rng_file' => 'RNG file "{$file}" does not exists or is not readable' + ); + + /** + * The name of the program, if not given it defaults to argv[0]. + * + * @var string $name Name of your program + */ + public $name; + + /** + * A description text that will be displayed in the help message. + * + * @var string $description Description of your program + */ + public $description = ''; + + /** + * A string that represents the version of the program, if this property is + * not empty and property add_version_option is not set to false, the + * command line parser will add a --version option, that will display the + * property content. + * + * @var string $version + * @access public + */ + public $version = ''; + + /** + * Boolean that determine if the command line parser should add the help + * (-h, --help) option automatically. + * + * @var bool $add_help_option Whether to add a help option or not + */ + public $add_help_option = true; + + /** + * Boolean that determine if the command line parser should add the version + * (-v, --version) option automatically. + * Note that the version option is also generated only if the version + * property is not empty, it's up to you to provide a version string of + * course. + * + * @var bool $add_version_option Whether to add a version option or not + */ + public $add_version_option = true; + + /** + * Boolean that determine if providing a subcommand is mandatory. + * + * @var bool $subcommand_required Whether a subcommand is required or not + */ + public $subcommand_required = false; + + /** + * The command line parser renderer instance. + * + * @var object that implements PEAR2\Console\CommandLine\Renderer interface + */ + public $renderer = false; + + /** + * The command line parser outputter instance. + * + * @var PEAR2\Console\CommandLine\Outputter An outputter + */ + public $outputter = false; + + /** + * The command line message provider instance. + * + * @var PEAR2\Console\CommandLine\MessageProvider A message provider instance + */ + public $message_provider = false; + + /** + * Boolean that tells the parser to be POSIX compliant, POSIX demands the + * following behavior: the first non-option stops option processing. + * + * @var bool $force_posix Whether to force posix compliance or not + */ + public $force_posix = false; + + /** + * Boolean that tells the parser to set relevant options default values, + * according to the option action. + * + * @see PEAR2\Console\CommandLine\Option::setDefaults() + * @var bool $force_options_defaults Whether to force option default values + */ + public $force_options_defaults = false; + + /** + * An array of PEAR2\Console\CommandLine\Option objects. + * + * @var array $options The options array + */ + public $options = array(); + + /** + * An array of PEAR2\Console\CommandLine\Argument objects. + * + * @var array $args The arguments array + */ + public $args = array(); + + /** + * An array of PEAR2\Console\CommandLine\Command objects (sub commands). + * + * @var array $commands The commands array + */ + public $commands = array(); + + /** + * Parent, only relevant in Command objects but left here for interface + * convenience. + * + * @var PEAR2\Console\CommandLine The parent instance + * @todo move CommandLine::parent to CommandLine\Command + */ + public $parent = false; + + /** + * Array of valid actions for an option, this array will also store user + * registered actions. + * + * The array format is: + *
+     * array(
+     *      => array(, )
+     * )
+     * 
+ * + * @var array $actions List of valid actions + */ + public static $actions = array( + 'StoreTrue' => array( + 'PEAR2\\Console\\CommandLine\\Action\\StoreTrue', true + ), + 'StoreFalse' => array( + 'PEAR2\\Console\\CommandLine\\Action\\StoreFalse', true + ), + 'StoreString' => array( + 'PEAR2\\Console\\CommandLine\\Action\\StoreString', true + ), + 'StoreInt' => array( + 'PEAR2\\Console\\CommandLine\\Action\\StoreInt', true + ), + 'StoreFloat' => array( + 'PEAR2\\Console\\CommandLine\\Action\\StoreFloat', true + ), + 'StoreArray' => array( + 'PEAR2\\Console\\CommandLine\\Action\\StoreArray', true + ), + 'Callback' => array( + 'PEAR2\\Console\\CommandLine\\Action\\Callback', true + ), + 'Counter' => array( + 'PEAR2\\Console\\CommandLine\\Action\\Counter', true + ), + 'Help' => array( + 'PEAR2\\Console\\CommandLine\\Action\\Help', true + ), + 'Version' => array( + 'PEAR2\\Console\\CommandLine\\Action\\Version', true + ), + 'Password' => array( + 'PEAR2\\Console\\CommandLine\\Action\\Password', true + ), + 'List' => array( + 'PEAR2\\Console\\CommandLine\\Action_List', true + ), + ); + + /** + * Custom errors messages for this command + * + * This array is of the form: + * + * $messageText, + * $messageName => $messageText, + * ... + * ); + * ?> + * + * + * If specified, these messages override the messages provided by the + * default message provider. For example: + * + * 'The argument foo is required.', + * ); + * ?> + * + * + * @var array + * @see PEAR2\Console\CommandLine\MessageProvider_Default + */ + public $messages = array(); + + // }}} + // {{{ Private properties + + /** + * Array of options that must be dispatched at the end. + * + * @var array $_dispatchLater Options to be dispatched + */ + private $_dispatchLater = array(); + + // }}} + // __construct() {{{ + + /** + * Constructor. + * Example: + * + * + * $parser = new PEAR2\Console\CommandLine(array( + * 'name' => 'yourprogram', // defaults to argv[0] + * 'description' => 'Description of your program', + * 'version' => '0.0.1', // your program version + * 'add_help_option' => true, // or false to disable --help option + * 'add_version_option' => true, // or false to disable --version option + * 'force_posix' => false // or true to force posix compliance + * )); + * + * + * @param array $params An optional array of parameters + * + * @return void + */ + public function __construct(array $params = array()) + { + if (isset($params['name'])) { + $this->name = $params['name']; + } else if (isset($argv) && count($argv) > 0) { + $this->name = $argv[0]; + } else if (isset($_SERVER['argv']) && count($_SERVER['argv']) > 0) { + $this->name = $_SERVER['argv'][0]; + } else if (isset($_SERVER['SCRIPT_NAME'])) { + $this->name = basename($_SERVER['SCRIPT_NAME']); + } + if (isset($params['description'])) { + $this->description = $params['description']; + } + if (isset($params['version'])) { + $this->version = $params['version']; + } + if (isset($params['add_version_option'])) { + $this->add_version_option = $params['add_version_option']; + } + if (isset($params['add_help_option'])) { + $this->add_help_option = $params['add_help_option']; + } + if (isset($params['subcommand_required'])) { + $this->subcommand_required = $params['subcommand_required']; + } + if (isset($params['force_posix'])) { + $this->force_posix = $params['force_posix']; + } else if (getenv('POSIXLY_CORRECT')) { + $this->force_posix = true; + } + if (isset($params['messages']) && is_array($params['messages'])) { + $this->messages = $params['messages']; + } + // set default instances + $this->renderer = new CommandLine\Renderer_Default($this); + $this->outputter = new CommandLine\Outputter_Default(); + $this->message_provider = new CommandLine\MessageProvider_Default(); + } + + // }}} + // accept() {{{ + + /** + * Method to allow PEAR2\Console\CommandLine to accept either: + * + a custom renderer, + * + a custom outputter, + * + or a custom message provider + * + * @param mixed $instance The custom instance + * + * @return void + * @throws PEAR2\Console\CommandLine\Exception if wrong argument passed + */ + public function accept($instance) + { + if ($instance instanceof CommandLine\Renderer) { + if (property_exists($instance, 'parser') && !$instance->parser) { + $instance->parser = $this; + } + $this->renderer = $instance; + } else if ($instance instanceof CommandLine\Outputter) { + $this->outputter = $instance; + } else if ($instance instanceof CommandLine\MessageProvider) { + $this->message_provider = $instance; + } else { + throw CommandLine\Exception::factory( + 'INVALID_CUSTOM_INSTANCE', + array(), + $this, + $this->messages + ); + } + } + + // }}} + // fromXmlFile() {{{ + + /** + * Returns a command line parser instance built from an xml file. + * + * Example: + * + * $parser = PEAR2\Console\CommandLine::fromXmlFile('path/to/file.xml'); + * $result = $parser->parse(); + * + * + * @param string $file Path to the xml file + * + * @return PEAR2\Console\CommandLine The parser instance + */ + public static function fromXmlFile($file) + { + return CommandLine\XmlParser::parse($file); + } + + // }}} + // fromXmlString() {{{ + + /** + * Returns a command line parser instance built from an xml string. + * + * Example: + * + * $xmldata = ' + * + * Compress files + * + * + * a list of files + * true + * + * '; + * $parser = PEAR2\Console\CommandLine::fromXmlString($xmldata); + * $result = $parser->parse(); + * + * + * @param string $string The xml data + * + * @return PEAR2\Console\CommandLine The parser instance + */ + public static function fromXmlString($string) + { + return CommandLine\XmlParser::parseString($string); + } + + // }}} + // addArgument() {{{ + + /** + * Adds an argument to the command line parser and returns it. + * + * Adds an argument with the name $name and set its attributes with the + * array $params, then return the PEAR2\Console\CommandLine\Argument instance + * created. + * The method accepts another form: you can directly pass a + * PEAR2\Console\CommandLine\Argument object as the sole argument, this allows + * you to contruct the argument separately, in order to reuse it in + * different command line parsers or commands for example. + * + * Example: + * + * $parser = new PEAR2\Console\CommandLine(); + * // add an array argument + * $parser->addArgument('input_files', array('multiple'=>true)); + * // add a simple argument + * $parser->addArgument('output_file'); + * $result = $parser->parse(); + * print_r($result->args['input_files']); + * print_r($result->args['output_file']); + * // will print: + * // array('file1', 'file2') + * // 'file3' + * // if the command line was: + * // myscript.php file1 file2 file3 + * + * + * In a terminal, the help will be displayed like this: + * + * $ myscript.php install -h + * Usage: myscript.php + * + * + * @param mixed $name A string containing the argument name or an + * instance of PEAR2\Console\CommandLine\Argument + * @param array $params An array containing the argument attributes + * + * @return PEAR2\Console\CommandLine\Argument the added argument + * @see PEAR2\Console\CommandLine\Argument + */ + public function addArgument($name, $params = array()) + { + if ($name instanceof CommandLine\Argument) { + $argument = $name; + } else { + $argument = new CommandLine\Argument($name, $params); + } + $argument->validate(); + $this->args[$argument->name] = $argument; + return $argument; + } + + // }}} + // addCommand() {{{ + + /** + * Adds a sub-command to the command line parser. + * + * Adds a command with the given $name to the parser and returns the + * PEAR2\Console\CommandLine\Command instance, you can then populate the command + * with options, configure it, etc... like you would do for the main parser + * because the class PEAR2\Console\CommandLine\Command inherits from + * PEAR2\Console\CommandLine. + * + * An example: + * + * $parser = new PEAR2\Console\CommandLine(); + * $install_cmd = $parser->addCommand('install'); + * $install_cmd->addOption( + * 'verbose', + * array( + * 'short_name' => '-v', + * 'long_name' => '--verbose', + * 'description' => 'be noisy when installing stuff', + * 'action' => 'StoreTrue' + * ) + * ); + * $parser->parse(); + * + * Then in a terminal: + * + * $ myscript.php install -h + * Usage: myscript.php install [options] + * + * Options: + * -h, --help display this help message and exit + * -v, --verbose be noisy when installing stuff + * + * $ myscript.php install --verbose + * Installing whatever... + * $ + * + * + * @param mixed $name A string containing the command name or an + * instance of PEAR2\Console\CommandLine\Command + * @param array $params An array containing the command attributes + * + * @return PEAR2\Console\CommandLine\Command The added subcommand + * @see PEAR2\Console\CommandLine\Command + */ + public function addCommand($name, $params = array()) + { + if ($name instanceof CommandLine\Command) { + $command = $name; + } else { + $params['name'] = $name; + $command = new CommandLine\Command($params); + // some properties must cascade to the child command if not + // passed explicitely. This is done only in this case, because if + // we have a Command object we have no way to determine if theses + // properties have already been set + $cascade = array( + 'add_help_option', + 'add_version_option', + 'outputter', + 'message_provider', + 'force_posix', + 'force_options_defaults' + ); + foreach ($cascade as $property) { + if (!isset($params[$property])) { + $command->$property = $this->$property; + } + } + if (!isset($params['renderer'])) { + $renderer = clone $this->renderer; + $renderer->parser = $command; + $command->renderer = $renderer; + } + } + $command->parent = $this; + $this->commands[$command->name] = $command; + return $command; + } + + // }}} + // addOption() {{{ + + /** + * Adds an option to the command line parser and returns it. + * + * Adds an option with the name $name and set its attributes with the + * array $params, then return the PEAR2\Console\CommandLine\Option instance + * created. + * The method accepts another form: you can directly pass a + * PEAR2\Console\CommandLine\Option object as the sole argument, this allows + * you to contruct the option separately, in order to reuse it in different + * command line parsers or commands for example. + * + * Example: + * + * $parser = new PEAR2\Console\CommandLine(); + * $parser->addOption('path', array( + * 'short_name' => '-p', // a short name + * 'long_name' => '--path', // a long name + * 'description' => 'path to the dir', // a description msg + * 'action' => 'StoreString', + * 'default' => '/tmp' // a default value + * )); + * $parser->parse(); + * + * + * In a terminal, the help will be displayed like this: + * + * $ myscript.php --help + * Usage: myscript.php [options] + * + * Options: + * -h, --help display this help message and exit + * -p, --path path to the dir + * + * + * + * Various methods to specify an option, these 3 commands are equivalent: + * + * $ myscript.php --path=some/path + * $ myscript.php -p some/path + * $ myscript.php -psome/path + * + * + * @param mixed $name A string containing the option name or an + * instance of PEAR2\Console\CommandLine\Option + * @param array $params An array containing the option attributes + * + * @return PEAR2\Console\CommandLine\Option The added option + * @see PEAR2\Console\CommandLine\Option + */ + public function addOption($name, $params = array()) + { + if ($name instanceof CommandLine\Option) { + $opt = $name; + } else { + $opt = new CommandLine\Option($name, $params); + } + $opt->validate(); + if ($this->force_options_defaults) { + $opt->setDefaults(); + } + $this->options[$opt->name] = $opt; + if (!empty($opt->choices) && $opt->add_list_option) { + $this->addOption( + 'list_' . $opt->name, + array( + 'long_name' => '--list-' . $opt->name, + 'description' => $this->message_provider->get( + 'LIST_OPTION_MESSAGE', + array('name' => $opt->name) + ), + 'action' => 'List', + 'action_params' => array('list' => $opt->choices), + ) + ); + } + return $opt; + } + + // }}} + // displayError() {{{ + + /** + * Displays an error to the user via stderr and exit with $exitCode if its + * value is not equals to false. + * + * @param string $error The error message + * @param int $exitCode The exit code number (default: 1). If set to + * false, the exit() function will not be called + * + * @return void + */ + public function displayError($error, $exitCode = 1) + { + $this->outputter->stderr($this->renderer->error($error)); + if ($exitCode !== false) { + exit($exitCode); + } + } + + // }}} + // displayUsage() {{{ + + /** + * Displays the usage help message to the user via stdout and exit with + * $exitCode if its value is not equals to false. + * + * @param int $exitCode The exit code number (default: 0). If set to + * false, the exit() function will not be called + * + * @return void + */ + public function displayUsage($exitCode = 0) + { + $this->outputter->stdout($this->renderer->usage()); + if ($exitCode !== false) { + exit($exitCode); + } + } + + // }}} + // displayVersion() {{{ + + /** + * Displays the program version to the user via stdout and exit with + * $exitCode if its value is not equals to false. + * + * @param int $exitCode The exit code number (default: 0). If set to + * false, the exit() function will not be called + * + * @return void + */ + public function displayVersion($exitCode = 0) + { + $this->outputter->stdout($this->renderer->version()); + if ($exitCode !== false) { + exit($exitCode); + } + } + + // }}} + // findOption() {{{ + + /** + * Finds the option that matches the given short_name (ex: -v), long_name + * (ex: --verbose) or name (ex: verbose). + * + * @param string $str The option identifier + * + * @return mixed A PEAR2\Console\CommandLine\Option instance or false + */ + public function findOption($str) + { + $str = trim($str); + if ($str === '') { + return false; + } + $matches = array(); + foreach ($this->options as $opt) { + if ($opt->short_name == $str + || $opt->long_name == $str + || $opt->name == $str + ) { + // exact match + return $opt; + } + if (substr($opt->long_name, 0, strlen($str)) === $str) { + // abbreviated long option + $matches[] = $opt; + } + } + if ($count = count($matches)) { + if ($count > 1) { + $matches_str = ''; + $padding = ''; + foreach ($matches as $opt) { + $matches_str .= $padding . $opt->long_name; + $padding = ', '; + } + throw CommandLine\Exception::factory( + 'OPTION_AMBIGUOUS', + array('name' => $str, 'matches' => $matches_str), + $this, + $this->messages + ); + } + return $matches[0]; + } + return false; + } + // }}} + // registerAction() {{{ + + /** + * Registers a custom action for the parser, an example: + * + * + * + * // in this example we create a "range" action: + * // the user will be able to enter something like: + * // $ -r 1,5 + * // and in the result we will have: + * // $result->options['range']: array(1, 5) + * + * class ActionRange extends PEAR2\Console\CommandLine\Action + * { + * public function execute($value=false, $params=array()) + * { + * $range = explode(',', str_replace(' ', '', $value)); + * if (count($range) != 2) { + * throw new Exception(sprintf( + * 'Option "%s" must be 2 integers separated by a comma', + * $this->option->name + * )); + * } + * $this->setResult($range); + * } + * } + * // then we can register our action + * PEAR2\Console\CommandLine::registerAction('Range', 'ActionRange'); + * // and now our action is available ! + * $parser = new PEAR2\Console\CommandLine(); + * $parser->addOption('range', array( + * 'short_name' => '-r', + * 'long_name' => '--range', + * 'action' => 'Range', // note our custom action + * 'description' => 'A range of two integers separated by a comma' + * )); + * // etc... + * + * + * + * @param string $name The name of the custom action + * @param string $class The class name of the custom action + * + * @return void + */ + public static function registerAction($name, $class) + { + if (!isset(self::$actions[$name])) { + if (!class_exists($class)) { + self::triggerError( + 'action_class_does_not_exists', + E_USER_ERROR, + array('{$name}' => $name, '{$class}' => $class) + ); + } + self::$actions[$name] = array($class, false); + } + } + + // }}} + // triggerError() {{{ + + /** + * A wrapper for programming errors triggering. + * + * @param string $msgId Identifier of the message + * @param int $level The php error level + * @param array $params An array of search=>replaces entries + * + * @return void + * @todo remove Console::triggerError() and use exceptions only + */ + public static function triggerError($msgId, $level, $params=array()) + { + if (isset(self::$errors[$msgId])) { + $msg = str_replace( + array_keys($params), + array_values($params), + self::$errors[$msgId] + ); + trigger_error($msg, $level); + } else { + trigger_error('unknown error', $level); + } + } + + // }}} + // parse() {{{ + + /** + * Parses the command line arguments and returns a + * PEAR2\Console\CommandLine\Result instance. + * + * @param integer $userArgc Number of arguments (optional) + * @param array $userArgv Array containing arguments (optional) + * + * @return PEAR2\Console\CommandLine\Result The result instance + * @throws Exception on user errors + */ + public function parse($userArgc=null, $userArgv=null) + { + $this->addBuiltinOptions(); + if ($userArgc !== null && $userArgv !== null) { + $argc = $userArgc; + $argv = $userArgv; + } else { + list($argc, $argv) = $this->getArgcArgv(); + } + // build an empty result + $result = new CommandLine\Result(); + if (!($this instanceof CommandLine\Command)) { + // remove script name if we're not in a subcommand + array_shift($argv); + $argc--; + } + // will contain arguments + $args = array(); + foreach ($this->options as $name=>$option) { + $result->options[$name] = $option->default; + } + // parse command line tokens + while ($argc--) { + $token = array_shift($argv); + try { + if ($cmd = $this->_getSubCommand($token)) { + $result->command_name = $cmd->name; + $result->command = $cmd->parse($argc, $argv); + break; + } else { + $this->parseToken($token, $result, $args, $argc); + } + } catch (Exception $exc) { + throw $exc; + } + } + // Parse a null token to allow any undespatched actions to be despatched. + $this->parseToken(null, $result, $args, 0); + // Check if an invalid subcommand was specified. If there are + // subcommands and no arguments, but an argument was provided, it is + // an invalid subcommand. + if ( count($this->commands) > 0 + && count($this->args) === 0 + && count($args) > 0 + ) { + throw CommandLine\Exception::factory( + 'INVALID_SUBCOMMAND', + array('command' => $args[0]), + $this, + $this->messages + ); + } + // if subcommand_required is set to true we must check that we have a + // subcommand. + if ( count($this->commands) + && $this->subcommand_required + && !$result->command_name + ) { + throw CommandLine\Exception::factory( + 'SUBCOMMAND_REQUIRED', + array('commands' => implode(array_keys($this->commands), ', ')), + $this, + $this->messages + ); + } + // minimum argument number check + $argnum = 0; + foreach ($this->args as $name=>$arg) { + if (!$arg->optional) { + $argnum++; + } + } + if (count($args) < $argnum) { + throw CommandLine\Exception::factory( + 'ARGUMENT_REQUIRED', + array('argnum' => $argnum, 'plural' => $argnum>1 ? 's': ''), + $this, + $this->messages + ); + } + // handle arguments + $c = count($this->args); + foreach ($this->args as $name=>$arg) { + $c--; + if ($arg->multiple) { + $result->args[$name] = $c ? array_splice($args, 0, -$c) : $args; + } else { + $result->args[$name] = array_shift($args); + } + if (!$result->args[$name] && $arg->optional && $arg->default) { + $result->args[$name] = $arg->default; + } + } + // dispatch deferred options + foreach ($this->_dispatchLater as $optArray) { + $optArray[0]->dispatchAction($optArray[1], $optArray[2], $this); + } + return $result; + } + + // }}} + // parseToken() {{{ + + /** + * Parses the command line token and modifies *by reference* the $options + * and $args arrays. + * + * @param string $token The command line token to parse + * @param object $result The PEAR2\Console\CommandLine\Result instance + * @param array &$args The argv array + * @param int $argc Number of lasting args + * + * @return void + * @access protected + * @throws Exception on user errors + */ + protected function parseToken($token, $result, &$args, $argc) + { + static $lastopt = false; + static $stopflag = false; + $last = $argc === 0; + if (!$stopflag && $lastopt) { + if (substr($token, 0, 1) == '-') { + if ($lastopt->argument_optional) { + $this->_dispatchAction($lastopt, '', $result); + if ($lastopt->action != 'StoreArray') { + $lastopt = false; + } + } else if (isset($result->options[$lastopt->name])) { + // case of an option that expect a list of args + $lastopt = false; + } else { + throw CommandLine\Exception::factory( + 'OPTION_VALUE_REQUIRED', + array('name' => $lastopt->name), + $this, + $this->messages + ); + } + } else { + // when a StoreArray option is positioned last, the behavior + // is to consider that if there's already an element in the + // array, and the commandline expects one or more args, we + // leave last tokens to arguments + if ($lastopt->action == 'StoreArray' + && !empty($result->options[$lastopt->name]) + && count($this->args) > ($argc + count($args)) + ) { + if (!is_null($token)) { + $args[] = $token; + } + return; + } + if (!is_null($token) || $lastopt->action == 'Password') { + $this->_dispatchAction($lastopt, $token, $result); + } + if ($lastopt->action != 'StoreArray') { + $lastopt = false; + } + return; + } + } + if (!$stopflag && substr($token, 0, 2) == '--') { + // a long option + $optkv = explode('=', $token, 2); + if (trim($optkv[0]) == '--') { + // the special argument "--" forces in all cases the end of + // option scanning. + $stopflag = true; + return; + } + $opt = $this->findOption($optkv[0]); + if (!$opt) { + throw CommandLine\Exception::factory( + 'OPTION_UNKNOWN', + array('name' => $optkv[0]), + $this, + $this->messages + ); + } + $value = isset($optkv[1]) ? $optkv[1] : false; + if (!$opt->expectsArgument() && $value !== false) { + throw CommandLine\Exception::factory( + 'OPTION_VALUE_UNEXPECTED', + array('name' => $opt->name, 'value' => $value), + $this, + $this->messages + ); + } + if ($opt->expectsArgument() && $value === false) { + // maybe the long option argument is separated by a space, if + // this is the case it will be the next arg + if ($last && !$opt->argument_optional) { + throw CommandLine\Exception::factory( + 'OPTION_VALUE_REQUIRED', + array('name' => $opt->name), + $this, + $this->messages + ); + } + // we will have a value next time + $lastopt = $opt; + return; + } + if ($opt->action == 'StoreArray') { + $lastopt = $opt; + } + $this->_dispatchAction($opt, $value, $result); + } else if (!$stopflag && substr($token, 0, 1) == '-') { + // a short option + $optname = substr($token, 0, 2); + if ($optname == '-') { + // special case of "-": try to read stdin + $args[] = file_get_contents('php://stdin'); + return; + } + $opt = $this->findOption($optname); + if (!$opt) { + throw CommandLine\Exception::factory( + 'OPTION_UNKNOWN', + array('name' => $optname), + $this, + $this->messages + ); + } + // parse other options or set the value + // in short: handle -f and -f + $next = substr($token, 2, 1); + // check if we must wait for a value + if ($next === false) { + if ($opt->expectsArgument()) { + if ($last && !$opt->argument_optional) { + throw CommandLine\Exception::factory( + 'OPTION_VALUE_REQUIRED', + array('name' => $opt->name), + $this, + $this->messages + ); + } + // we will have a value next time + $lastopt = $opt; + return; + } + $value = false; + } else { + if (!$opt->expectsArgument()) { + if ($nextopt = $this->findOption('-' . $next)) { + $this->_dispatchAction($opt, false, $result); + $this->parseToken( + '-' . substr($token, 2), + $result, + $args, + $last + ); + return; + } else { + throw CommandLine\Exception::factory( + 'OPTION_UNKNOWN', + array('name' => $next), + $this, + $this->messages + ); + } + } + if ($opt->action == 'StoreArray') { + $lastopt = $opt; + } + $value = substr($token, 2); + } + $this->_dispatchAction($opt, $value, $result); + } else { + // We have an argument. + // if we are in POSIX compliant mode, we must set the stop flag to + // true in order to stop option parsing. + if (!$stopflag && $this->force_posix) { + $stopflag = true; + } + if (!is_null($token)) { + $args[] = $token; + } + } + } + + // }}} + // addBuiltinOptions() {{{ + + /** + * Adds the builtin "Help" and "Version" options if needed. + * + * @return void + */ + public function addBuiltinOptions() + { + if ($this->add_help_option) { + $helpOptionParams = array( + 'long_name' => '--help', + 'description' => 'show this help message and exit', + 'action' => 'Help' + ); + if (!($option = $this->findOption('-h')) || $option->action == 'Help') { + // short name is available, take it + $helpOptionParams['short_name'] = '-h'; + } + $this->addOption('help', $helpOptionParams); + } + if ($this->add_version_option && !empty($this->version)) { + $versionOptionParams = array( + 'long_name' => '--version', + 'description' => 'show the program version and exit', + 'action' => 'Version' + ); + if (!$this->findOption('-v')) { + // short name is available, take it + $versionOptionParams['short_name'] = '-v'; + } + $this->addOption('version', $versionOptionParams); + } + } + + // }}} + // getArgcArgv() {{{ + + /** + * Tries to return an array containing argc and argv, or trigger an error + * if it fails to get them. + * + * @return array The argc/argv array + * @throws PEAR2\Console\CommandLine\Exception + */ + protected function getArgcArgv() + { + if (php_sapi_name() != 'cli') { + // we have a web request + $argv = array($this->name); + if (isset($_REQUEST)) { + foreach ($_REQUEST as $key => $value) { + if (!is_array($value)) { + $value = array($value); + } + $opt = $this->findOption($key); + if ($opt instanceof CommandLine\Option) { + // match a configured option + $argv[] = $opt->short_name ? + $opt->short_name : $opt->long_name; + foreach ($value as $v) { + if ($opt->expectsArgument()) { + $argv[] = isset($_REQUEST[$key]) + ? urldecode($v) + : $v; + } else if ($v == '0' || $v == 'false') { + array_pop($argv); + } + } + } else if (isset($this->args[$key])) { + // match a configured argument + foreach ($value as $v) { + $argv[] = isset($_REQUEST[$key]) ? urldecode($v) : $v; + } + } + } + } + return array(count($argv), $argv); + } + if (isset($argc) && isset($argv)) { + // case of register_argv_argc = 1 + return array($argc, $argv); + } + if (isset($_SERVER['argc']) && isset($_SERVER['argv'])) { + return array($_SERVER['argc'], $_SERVER['argv']); + } + return array(0, array()); + } + + // }}} + // _dispatchAction() {{{ + + /** + * Dispatches the given option or store the option to dispatch it later. + * + * @param PEAR2\Console\CommandLine\Option $option The option instance + * @param string $token Command line token to parse + * @param PEAR2\Console\CommandLine\Result $result The result instance + * + * @return void + */ + private function _dispatchAction($option, $token, $result) + { + if ($option->action == 'Password') { + $this->_dispatchLater[] = array($option, $token, $result); + } else { + $option->dispatchAction($token, $result, $this); + } + } + // }}} + // _getSubCommand() {{{ + + /** + * Tries to return the subcommand that matches the given token or returns + * false if no subcommand was found. + * + * @param string $token Current command line token + * + * @return mixed An instance of PEAR2\Console\CommandLine\Command or false + */ + private function _getSubCommand($token) + { + foreach ($this->commands as $cmd) { + if ($cmd->name == $token || in_array($token, $cmd->aliases)) { + return $cmd; + } + } + return false; + } + + // }}} +} diff --git a/system/autoload/PEAR2/Console/CommandLine/Action.php b/system/autoload/PEAR2/Console/CommandLine/Action.php new file mode 100644 index 0000000..2aca60a --- /dev/null +++ b/system/autoload/PEAR2/Console/CommandLine/Action.php @@ -0,0 +1,142 @@ + + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 0.2.1 + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since File available since release 0.1.0 + * @filesource + */ +namespace PEAR2\Console\CommandLine; + +/** + * Class that represent an option action. + * + * @category Console + * @package PEAR2\Console\CommandLine + * @author David JEAN LOUIS + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since Class available since release 0.1.0 + */ +abstract class Action +{ + // Properties {{{ + + /** + * A reference to the result instance. + * + * @var PEAR2\Console\CommandLine_Result $result The result instance + */ + protected $result; + + /** + * A reference to the option instance. + * + * @var PEAR2\Console\CommandLine_Option $option The action option + */ + protected $option; + + /** + * A reference to the parser instance. + * + * @var PEAR2\Console\CommandLine $parser The parser + */ + protected $parser; + + // }}} + // __construct() {{{ + + /** + * Constructor + * + * @param PEAR2\Console\CommandLine_Result $result The result instance + * @param PEAR2\Console\CommandLine_Option $option The action option + * @param PEAR2\Console\CommandLine $parser The current parser + * + * @return void + */ + public function __construct($result, $option, $parser) + { + $this->result = $result; + $this->option = $option; + $this->parser = $parser; + } + + // }}} + // getResult() {{{ + + /** + * Convenience method to retrieve the value of result->options[name]. + * + * @return mixed The result value or null + */ + public function getResult() + { + if (isset($this->result->options[$this->option->name])) { + return $this->result->options[$this->option->name]; + } + return null; + } + + // }}} + // format() {{{ + + /** + * Allow a value to be pre-formatted prior to being used in a choices test. + * Setting $value to the new format will keep the formatting. + * + * @param mixed &$value The value to format + * + * @return mixed The formatted value + */ + public function format(&$value) + { + return $value; + } + + // }}} + // setResult() {{{ + + /** + * Convenience method to assign the result->options[name] value. + * + * @param mixed $result The result value + * + * @return void + */ + public function setResult($result) + { + $this->result->options[$this->option->name] = $result; + } + + // }}} + // execute() {{{ + + /** + * Executes the action with the value entered by the user. + * All children actions must implement this method. + * + * @param mixed $value The option value + * @param array $params An optional array of parameters + * + * @return string + */ + abstract public function execute($value = false, $params = array()); + // }}} +} diff --git a/system/autoload/PEAR2/Console/CommandLine/Action/Callback.php b/system/autoload/PEAR2/Console/CommandLine/Action/Callback.php new file mode 100644 index 0000000..a5edd7a --- /dev/null +++ b/system/autoload/PEAR2/Console/CommandLine/Action/Callback.php @@ -0,0 +1,86 @@ + + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 0.2.1 + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since File available since release 0.1.0 + * @filesource + */ +namespace PEAR2\Console\CommandLine\Action; + +use PEAR2\Console\CommandLine; + + +/** + * Class that represent the Callback action. + * + * The result option array entry value is set to the return value of the + * callback defined in the option. + * + * There are two steps to defining a callback option: + * - define the option itself using the callback action + * - write the callback; this is a function (or method) that takes five + * arguments, as described below. + * + * All callbacks are called as follows: + * + * callable_func( + * $value, // the value of the option + * $option_instance, // the option instance + * $result_instance, // the result instance + * $parser_instance, // the parser instance + * $params // an array of params as specified in the option + * ); + * + * and *must* return the option value. + * + * @category Console + * @package PEAR2\Console\CommandLine + * @author David JEAN LOUIS + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since Class available since release 0.1.0 + */ +class Callback extends CommandLine\Action +{ + // execute() {{{ + + /** + * Executes the action with the value entered by the user. + * + * @param mixed $value The value of the option + * @param array $params An optional array of parameters + * + * @return string + */ + public function execute($value = false, $params = array()) + { + $this->setResult( + call_user_func( + $this->option->callback, + $value, + $this->option, + $this->result, + $this->parser, + $params + ) + ); + } + // }}} +} diff --git a/system/autoload/PEAR2/Console/CommandLine/Action/Counter.php b/system/autoload/PEAR2/Console/CommandLine/Action/Counter.php new file mode 100644 index 0000000..fd2adb9 --- /dev/null +++ b/system/autoload/PEAR2/Console/CommandLine/Action/Counter.php @@ -0,0 +1,84 @@ + + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 0.2.1 + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since File available since release 0.1.0 + * @filesource + */ + +namespace PEAR2\Console\CommandLine\Action; + +use PEAR2\Console\CommandLine; + +/** + * Class that represent the Version action. + * + * The execute methode add 1 to the value of the result option array entry. + * The value is incremented each time the option is found, for example + * with an option defined like that: + * + * + * $parser->addOption( + * 'verbose', + * array( + * 'short_name' => '-v', + * 'action' => 'Counter' + * ) + * ); + * + * If the user type: + * + * $ script.php -v -v -v + * + * or: + * + * $ script.php -vvv + * + * the verbose variable will be set to to 3. + * + * @category Console + * @package PEAR2\Console\CommandLine + * @author David JEAN LOUIS + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since Class available since release 0.1.0 + */ +class Counter extends CommandLine\Action +{ + // execute() {{{ + + /** + * Executes the action with the value entered by the user. + * + * @param mixed $value The option value + * @param array $params An optional array of parameters + * + * @return string + */ + public function execute($value = false, $params = array()) + { + $result = $this->getResult(); + if ($result === null) { + $result = 0; + } + $this->setResult(++$result); + } + // }}} +} diff --git a/system/autoload/PEAR2/Console/CommandLine/Action/Help.php b/system/autoload/PEAR2/Console/CommandLine/Action/Help.php new file mode 100644 index 0000000..b4a733a --- /dev/null +++ b/system/autoload/PEAR2/Console/CommandLine/Action/Help.php @@ -0,0 +1,58 @@ + + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 0.2.1 + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since File available since release 0.1.0 + * @filesource + */ + +namespace PEAR2\Console\CommandLine\Action; + +use PEAR2\Console\CommandLine; + +/** + * Class that represent the Help action, a special action that displays the + * help message, telling the user how to use the program. + * + * @category Console + * @package PEAR2\Console\CommandLine + * @author David JEAN LOUIS + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since Class available since release 0.1.0 + */ +class Help extends CommandLine\Action +{ + // execute() {{{ + + /** + * Executes the action with the value entered by the user. + * + * @param mixed $value The option value + * @param array $params An optional array of parameters + * + * @return string + */ + public function execute($value = false, $params = array()) + { + return $this->parser->displayUsage(); + } + // }}} +} diff --git a/system/autoload/PEAR2/Console/CommandLine/Action/List.php b/system/autoload/PEAR2/Console/CommandLine/Action/List.php new file mode 100644 index 0000000..3419f29 --- /dev/null +++ b/system/autoload/PEAR2/Console/CommandLine/Action/List.php @@ -0,0 +1,69 @@ + + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version CVS: $Id: List.php,v 1.2 2009/02/27 08:03:17 izi Exp $ + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since File available since release 0.1.0 + * @filesource + */ + +namespace PEAR2\Console\CommandLine; + +/** + * Class that represent the List action, a special action that simply output an + * array as a list. + * + * @category Console + * @package PEAR2\Console\CommandLine + * @author David JEAN LOUIS + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since Class available since release 0.1.0 + */ +class Action_List extends Action +{ + // execute() {{{ + + /** + * Executes the action with the value entered by the user. + * Possible parameters are: + * - message: an alternative message to display instead of the default + * message, + * - delimiter: an alternative delimiter instead of the comma, + * - post: a string to append after the message (default is the new line + * char). + * + * @param mixed $value The option value + * @param array $params An optional array of parameters + * + * @return string + */ + public function execute($value = false, $params = array()) + { + $list = isset($params['list']) ? $params['list'] : array(); + $msg = isset($params['message']) + ? $params['message'] + : $this->parser->message_provider->get('LIST_DISPLAYED_MESSAGE'); + $del = isset($params['delimiter']) ? $params['delimiter'] : ', '; + $post = isset($params['post']) ? $params['post'] : "\n"; + $this->parser->outputter->stdout($msg . implode($del, $list) . $post); + exit(0); + } + // }}} +} diff --git a/system/autoload/PEAR2/Console/CommandLine/Action/Password.php b/system/autoload/PEAR2/Console/CommandLine/Action/Password.php new file mode 100644 index 0000000..ad82539 --- /dev/null +++ b/system/autoload/PEAR2/Console/CommandLine/Action/Password.php @@ -0,0 +1,90 @@ + + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 0.2.1 + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since File available since release 0.1.0 + * @filesource + */ + +namespace PEAR2\Console\CommandLine\Action; + +use PEAR2\Console\CommandLine; + +/** + * Class that represent the Password action, a special action that allow the + * user to specify the password on the commandline or to be prompted for + * entering it. + * + * @category Console + * @package PEAR2\Console\CommandLine + * @author David JEAN LOUIS + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since Class available since release 0.1.0 + */ +class Password extends CommandLine\Action +{ + // execute() {{{ + + /** + * Executes the action with the value entered by the user. + * + * @param mixed $value The option value + * @param array $params An array of optional parameters + * + * @return string + */ + public function execute($value = false, $params = array()) + { + $this->setResult(empty($value) ? $this->_promptPassword() : $value); + } + // }}} + // _promptPassword() {{{ + + /** + * Prompts the password to the user without echoing it. + * + * @return string + * @todo not echo-ing the password does not work on windows is there a way + * to make this work ? + */ + private function _promptPassword() + { + if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { + fwrite( + STDOUT, + $this->parser->message_provider->get('PASSWORD_PROMPT_ECHO') + ); + @flock(STDIN, LOCK_EX); + $passwd = fgets(STDIN); + @flock(STDIN, LOCK_UN); + } else { + fwrite(STDOUT, $this->parser->message_provider->get('PASSWORD_PROMPT')); + // disable echoing + system('stty -echo'); + @flock(STDIN, LOCK_EX); + $passwd = fgets(STDIN); + @flock(STDIN, LOCK_UN); + system('stty echo'); + } + return trim($passwd); + } + // }}} +} diff --git a/system/autoload/PEAR2/Console/CommandLine/Action/StoreArray.php b/system/autoload/PEAR2/Console/CommandLine/Action/StoreArray.php new file mode 100644 index 0000000..af9c8d4 --- /dev/null +++ b/system/autoload/PEAR2/Console/CommandLine/Action/StoreArray.php @@ -0,0 +1,76 @@ + + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 0.2.1 + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since File available since release 0.1.0 + * @filesource + */ + +namespace PEAR2\Console\CommandLine\Action; + +use PEAR2\Console\CommandLine; + +/** + * Class that represent the StoreArray action. + * + * The execute method appends the value of the option entered by the user to + * the result option array entry. + * + * @category Console + * @package PEAR2\Console\CommandLine + * @author David JEAN LOUIS + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since Class available since release 0.1.0 + */ +class StoreArray extends CommandLine\Action +{ + // Protected properties {{{ + + /** + * Force a clean result when first called, overriding any defaults assigned. + * + * @var object $firstPass First time this action has been called. + */ + protected $firstPass = true; + + // }}} + // execute() {{{ + + /** + * Executes the action with the value entered by the user. + * + * @param mixed $value The option value + * @param array $params An optional array of parameters + * + * @return string + */ + public function execute($value = false, $params = array()) + { + $result = $this->getResult(); + if (null === $result || $this->firstPass) { + $result = array(); + $this->firstPass = false; + } + $result[] = $value; + $this->setResult($result); + } + // }}} +} diff --git a/system/autoload/PEAR2/Console/CommandLine/Action/StoreFalse.php b/system/autoload/PEAR2/Console/CommandLine/Action/StoreFalse.php new file mode 100644 index 0000000..20e8cbd --- /dev/null +++ b/system/autoload/PEAR2/Console/CommandLine/Action/StoreFalse.php @@ -0,0 +1,62 @@ + + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 0.2.1 + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since File available since release 0.1.0 + * @filesource + */ + +namespace PEAR2\Console\CommandLine\Action; + +use PEAR2\Console\CommandLine; + +/** + * Class that represent the StoreFalse action. + * + * The execute method store the boolean 'false' in the corrsponding result + * option array entry (the value is true if the option is not present in the + * command line entered by the user). + * + * @category Console + * @package PEAR2\Console\CommandLine + * @author David JEAN LOUIS + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since Class available since release 0.1.0 + */ +class StoreFalse extends CommandLine\Action +{ + // execute() {{{ + + /** + * Executes the action with the value entered by the user. + * + * @param mixed $value The option value + * @param array $params An array of optional parameters + * + * @return string + */ + public function execute($value = false, $params = array()) + { + $this->setResult(false); + } + + // }}} +} diff --git a/system/autoload/PEAR2/Console/CommandLine/Action/StoreFloat.php b/system/autoload/PEAR2/Console/CommandLine/Action/StoreFloat.php new file mode 100644 index 0000000..4242f9b --- /dev/null +++ b/system/autoload/PEAR2/Console/CommandLine/Action/StoreFloat.php @@ -0,0 +1,73 @@ + + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 0.2.1 + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since File available since release 0.1.0 + * @filesource + */ + +namespace PEAR2\Console\CommandLine\Action; + +use PEAR2\Console\CommandLine; + +/** + * Class that represent the StoreFloat action. + * + * The execute method store the value of the option entered by the user as a + * float in the result option array entry, if the value passed is not a float + * an Exception is raised. + * + * @category Console + * @package PEAR2\Console\CommandLine + * @author David JEAN LOUIS + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since Class available since release 0.1.0 + */ +class StoreFloat extends CommandLine\Action +{ + // execute() {{{ + + /** + * Executes the action with the value entered by the user. + * + * @param mixed $value The option value + * @param array $params An array of optional parameters + * + * @return string + * @throws PEAR2\Console\CommandLine\Exception + */ + public function execute($value = false, $params = array()) + { + if (!is_numeric($value)) { + throw CommandLine\Exception::factory( + 'OPTION_VALUE_TYPE_ERROR', + array( + 'name' => $this->option->name, + 'type' => 'float', + 'value' => $value + ), + $this->parser + ); + } + $this->setResult((float)$value); + } + // }}} +} diff --git a/system/autoload/PEAR2/Console/CommandLine/Action/StoreInt.php b/system/autoload/PEAR2/Console/CommandLine/Action/StoreInt.php new file mode 100644 index 0000000..af92024 --- /dev/null +++ b/system/autoload/PEAR2/Console/CommandLine/Action/StoreInt.php @@ -0,0 +1,73 @@ + + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 0.2.1 + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since File available since release 0.1.0 + * @filesource + */ + +namespace PEAR2\Console\CommandLine\Action; + +use PEAR2\Console\CommandLine; + +/** + * Class that represent the StoreInt action. + * + * The execute method store the value of the option entered by the user as an + * integer in the result option array entry, if the value passed is not an + * integer an Exception is raised. + * + * @category Console + * @package PEAR2\Console\CommandLine + * @author David JEAN LOUIS + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since Class available since release 0.1.0 + */ +class StoreInt extends CommandLine\Action +{ + // execute() {{{ + + /** + * Executes the action with the value entered by the user. + * + * @param mixed $value The option value + * @param array $params An array of optional parameters + * + * @return string + * @throws PEAR2\Console\CommandLine\Exception + */ + public function execute($value = false, $params = array()) + { + if (!is_numeric($value)) { + throw CommandLine\Exception::factory( + 'OPTION_VALUE_TYPE_ERROR', + array( + 'name' => $this->option->name, + 'type' => 'int', + 'value' => $value + ), + $this->parser + ); + } + $this->setResult((int)$value); + } + // }}} +} diff --git a/system/autoload/PEAR2/Console/CommandLine/Action/StoreString.php b/system/autoload/PEAR2/Console/CommandLine/Action/StoreString.php new file mode 100644 index 0000000..8e77e8a --- /dev/null +++ b/system/autoload/PEAR2/Console/CommandLine/Action/StoreString.php @@ -0,0 +1,60 @@ + + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 0.2.1 + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since File available since release 0.1.0 + * @filesource + */ + +namespace PEAR2\Console\CommandLine\Action; + +use PEAR2\Console\CommandLine; + +/** + * Class that represent the StoreString action. + * + * The execute method store the value of the option entered by the user as a + * string in the result option array entry. + * + * @category Console + * @package PEAR2\Console\CommandLine + * @author David JEAN LOUIS + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since Class available since release 0.1.0 + */ +class StoreString extends CommandLine\Action +{ + // execute() {{{ + + /** + * Executes the action with the value entered by the user. + * + * @param mixed $value The option value + * @param array $params An array of optional parameters + * + * @return string + */ + public function execute($value = false, $params = array()) + { + $this->setResult((string)$value); + } + // }}} +} diff --git a/system/autoload/PEAR2/Console/CommandLine/Action/StoreTrue.php b/system/autoload/PEAR2/Console/CommandLine/Action/StoreTrue.php new file mode 100644 index 0000000..419bc61 --- /dev/null +++ b/system/autoload/PEAR2/Console/CommandLine/Action/StoreTrue.php @@ -0,0 +1,61 @@ + + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 0.2.1 + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since File available since release 0.1.0 + * @filesource + */ + +namespace PEAR2\Console\CommandLine\Action; + +use PEAR2\Console\CommandLine; + +/** + * Class that represent the StoreTrue action. + * + * The execute method store the boolean 'true' in the corrsponding result + * option array entry (the value is false if the option is not present in the + * command line entered by the user). + * + * @category Console + * @package PEAR2\Console\CommandLine + * @author David JEAN LOUIS + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since Class available since release 0.1.0 + */ +class StoreTrue extends CommandLine\Action +{ + // execute() {{{ + + /** + * Executes the action with the value entered by the user. + * + * @param mixed $value The option value + * @param array $params An array of optional parameters + * + * @return string + */ + public function execute($value = false, $params = array()) + { + $this->setResult(true); + } + // }}} +} diff --git a/system/autoload/PEAR2/Console/CommandLine/Action/Version.php b/system/autoload/PEAR2/Console/CommandLine/Action/Version.php new file mode 100644 index 0000000..b31600d --- /dev/null +++ b/system/autoload/PEAR2/Console/CommandLine/Action/Version.php @@ -0,0 +1,58 @@ + + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 0.2.1 + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since File available since release 0.1.0 + * @filesource + */ + +namespace PEAR2\Console\CommandLine\Action; + +use PEAR2\Console\CommandLine; + +/** + * Class that represent the Version action, a special action that displays the + * version string of the program. + * + * @category Console + * @package PEAR2\Console\CommandLine + * @author David JEAN LOUIS + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since Class available since release 0.1.0 + */ +class Version extends CommandLine\Action +{ + // execute() {{{ + + /** + * Executes the action with the value entered by the user. + * + * @param mixed $value The option value + * @param array $params An array of optional parameters + * + * @return string + */ + public function execute($value = false, $params = array()) + { + return $this->parser->displayVersion(); + } + // }}} +} diff --git a/system/autoload/PEAR2/Console/CommandLine/Argument.php b/system/autoload/PEAR2/Console/CommandLine/Argument.php new file mode 100644 index 0000000..1e02302 --- /dev/null +++ b/system/autoload/PEAR2/Console/CommandLine/Argument.php @@ -0,0 +1,94 @@ + + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 0.2.1 + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since File available since release 0.1.0 + * @filesource + */ + +namespace PEAR2\Console\CommandLine; + +/** + * Class that represent a command line argument. + * + * @category Console + * @package PEAR2\Console\CommandLine + * @author David JEAN LOUIS + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since Class available since release 0.1.0 + */ +class Argument extends Element +{ + // Public properties {{{ + + /** + * Setting this to true will tell the parser that the argument expects more + * than one argument and that argument values should be stored in an array. + * + * @var boolean $multiple Whether the argument expects multiple values + */ + public $multiple = false; + + /** + * Setting this to true will tell the parser that the argument is optional + * and can be ommited. + * Note that it is not a good practice to make arguments optional, it is + * the role of the options to be optional, by essence. + * + * @var boolean $optional Whether the argument is optional or not. + */ + public $optional = false; + + // }}} + // validate() {{{ + + /** + * Validates the argument instance. + * + * @return void + * @throws PEAR2\Console\CommandLine\Exception + * @todo use exceptions + */ + public function validate() + { + // check if the argument name is valid + if (!preg_match( + '/^[a-zA-Z_\x7f-\xff]+[a-zA-Z0-9_\x7f-\xff]*$/', + $this->name + ) + ) { + \PEAR2\Console\CommandLine::triggerError( + 'argument_bad_name', + E_USER_ERROR, + array('{$name}' => $this->name) + ); + } + if (!$this->optional && $this->default !== null) { + \PEAR2\Console\CommandLine::triggerError( + 'argument_no_default', + E_USER_ERROR + ); + } + parent::validate(); + } + + // }}} +} diff --git a/system/autoload/PEAR2/Console/CommandLine/Command.php b/system/autoload/PEAR2/Console/CommandLine/Command.php new file mode 100644 index 0000000..2d00a47 --- /dev/null +++ b/system/autoload/PEAR2/Console/CommandLine/Command.php @@ -0,0 +1,72 @@ + + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 0.2.1 + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since File available since release 0.1.0 + * @filesource + */ + +namespace PEAR2\Console\CommandLine; + +/** + * Class that represent a command with option and arguments. + * + * This class exist just to clarify the interface but at the moment it is + * strictly identical to PEAR2\Console\CommandLine class, it could change in the + * future though. + * + * @category Console + * @package PEAR2\Console\CommandLine + * @author David JEAN LOUIS + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since Class available since release 0.1.0 + */ +class Command extends \PEAR2\Console\CommandLine +{ + // Public properties {{{ + + /** + * An array of aliases for the subcommand. + * + * @var array $aliases Aliases for the subcommand. + */ + public $aliases = array(); + + // }}} + // __construct() {{{ + + /** + * Constructor. + * + * @param array $params An optional array of parameters + * + * @return void + */ + public function __construct($params = array()) + { + if (isset($params['aliases'])) { + $this->aliases = $params['aliases']; + } + parent::__construct($params); + } + + // }}} +} diff --git a/system/autoload/PEAR2/Console/CommandLine/CustomMessageProvider.php b/system/autoload/PEAR2/Console/CommandLine/CustomMessageProvider.php new file mode 100644 index 0000000..c822faa --- /dev/null +++ b/system/autoload/PEAR2/Console/CommandLine/CustomMessageProvider.php @@ -0,0 +1,67 @@ + + * @author Michael Gauthier + * @copyright 2007 David JEAN LOUIS, 2009 silverorange + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version CVS: $Id: CustomMessageProvider.php 282427 2009-06-19 10:22:48Z izi $ + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since File available since release 1.1.0 + * @filesource + */ + +namespace PEAR2\Console\CommandLine; + +/** + * Common interfacefor message providers that allow overriding with custom + * messages + * + * Message providers may optionally implement this interface. + * + * @category Console + * @package PEAR2\Console\CommandLine + * @author David JEAN LOUIS + * @author Michael Gauthier + * @copyright 2007 David JEAN LOUIS, 2009 silverorange + * @license http://opensource.org/licenses/mit-license.php MIT License + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since Interface available since release 1.1.0 + */ +interface CustomMessageProvider +{ + // getWithCustomMesssages() {{{ + + /** + * Retrieves the given string identifier corresponding message. + * + * For a list of identifiers please see the provided default message + * provider. + * + * @param string $code The string identifier of the message + * @param array $vars An array of template variables + * @param array $messages An optional array of messages to use. Array + * indexes are message codes. + * + * @return string + * @see PEAR2\Console\CommandLine_MessageProvider + * @see PEAR2\Console\CommandLine_MessageProvider_Default + */ + public function getWithCustomMessages( + $code, $vars = array(), $messages = array() + ); + + // }}} +} diff --git a/system/autoload/PEAR2/Console/CommandLine/Element.php b/system/autoload/PEAR2/Console/CommandLine/Element.php new file mode 100644 index 0000000..825606a --- /dev/null +++ b/system/autoload/PEAR2/Console/CommandLine/Element.php @@ -0,0 +1,151 @@ + + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 0.2.1 + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since File available since release 0.1.0 + * @filesource + */ + +namespace PEAR2\Console\CommandLine; + +/** + * Class that represent a command line element (an option, or an argument). + * + * @category Console + * @package PEAR2\Console\CommandLine + * @author David JEAN LOUIS + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since Class available since release 0.1.0 + */ +abstract class Element +{ + // Public properties {{{ + + /** + * The element name. + * + * @var string $name Element name + */ + public $name; + + /** + * The name of variable displayed in the usage message, if no set it + * defaults to the "name" property. + * + * @var string $help_name Element "help" variable name + */ + public $help_name; + + /** + * The element description. + * + * @var string $description Element description + */ + public $description; + /** + * The default value of the element if not provided on the command line. + * + * @var mixed $default Default value of the option. + */ + public $default; + + /** + * Custom errors messages for this element + * + * This array is of the form: + * + * $messageText, + * $messageName => $messageText, + * ... + * ); + * ?> + * + * + * If specified, these messages override the messages provided by the + * default message provider. For example: + * + * 'The argument foo is required.', + * ); + * ?> + * + * + * @var array + * @see PEAR2\Console\CommandLine_MessageProvider_Default + */ + public $messages = array(); + + // }}} + // __construct() {{{ + + /** + * Constructor. + * + * @param string $name The name of the element + * @param array $params An optional array of parameters + * + * @return void + */ + public function __construct($name = null, $params = array()) + { + $this->name = $name; + foreach ($params as $attr => $value) { + if (property_exists($this, $attr)) { + $this->$attr = $value; + } + } + } + + // }}} + // toString() {{{ + + /** + * Returns the string representation of the element. + * + * @return string The string representation of the element + * @todo use __toString() instead + */ + public function toString() + { + return $this->help_name; + } + // }}} + // validate() {{{ + + /** + * Validates the element instance and set it's default values. + * + * @return void + * @throws PEAR2\Console\CommandLine\Exception + */ + public function validate() + { + // if no help_name passed, default to name + if ($this->help_name == null) { + $this->help_name = $this->name; + } + } + + // }}} +} diff --git a/system/autoload/PEAR2/Console/CommandLine/Exception.php b/system/autoload/PEAR2/Console/CommandLine/Exception.php new file mode 100644 index 0000000..6528f5d --- /dev/null +++ b/system/autoload/PEAR2/Console/CommandLine/Exception.php @@ -0,0 +1,90 @@ + + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 0.2.1 + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since File available since release 0.1.0 + * @filesource + */ + +namespace PEAR2\Console\CommandLine; + +use Exception as E; + +/** + * Class for exceptions raised by the PEAR2\Console\CommandLine package. + * + * @category Console + * @package PEAR2\Console\CommandLine + * @author David JEAN LOUIS + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since Class available since release 0.1.0 + */ +class Exception extends E +{ + // Codes constants {{{ + + /**#@+ + * Exception code constants. + */ + const OPTION_VALUE_REQUIRED = 1; + const OPTION_VALUE_UNEXPECTED = 2; + const OPTION_VALUE_TYPE_ERROR = 3; + const OPTION_UNKNOWN = 4; + const ARGUMENT_REQUIRED = 5; + const INVALID_SUBCOMMAND = 6; + /**#@-*/ + + // }}} + // factory() {{{ + + /** + * Convenience method that builds the exception with the array of params by + * calling the message provider class. + * + * @param string $code The string identifier of the + * exception. + * @param array $params Array of template vars/values + * @param PEAR2\Console\CommandLine $parser An instance of the parser + * @param array $messages An optional array of messages + * passed to the message provider. + * + * @return PEAR2\Console\CommandLine\Exception The exception instance + */ + public static function factory( + $code, $params, $parser, array $messages = array() + ) { + $provider = $parser->message_provider; + if ($provider instanceof CommandLine\CustomMessageProvider) { + $msg = $provider->getWithCustomMessages( + $code, + $params, + $messages + ); + } else { + $msg = $provider->get($code, $params); + } + $const = '\PEAR2\Console\CommandLine\Exception::' . $code; + $code = defined($const) ? constant($const) : 0; + return new static($msg, $code); + } + + // }}} +} diff --git a/system/autoload/PEAR2/Console/CommandLine/MessageProvider.php b/system/autoload/PEAR2/Console/CommandLine/MessageProvider.php new file mode 100644 index 0000000..9181326 --- /dev/null +++ b/system/autoload/PEAR2/Console/CommandLine/MessageProvider.php @@ -0,0 +1,57 @@ + + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 0.2.1 + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since File available since release 0.1.0 + * @filesource + */ + +namespace PEAR2\Console\CommandLine; + +/** + * Message providers common interface, all message providers must implement + * this interface. + * + * @category Console + * @package PEAR2\Console\CommandLine + * @author David JEAN LOUIS + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since Class available since release 0.1.0 + */ +interface MessageProvider +{ + // get() {{{ + + /** + * Retrieves the given string identifier corresponding message. + * For a list of identifiers please see the provided default message + * provider. + * + * @param string $code The string identifier of the message + * @param array $vars An array of template variables + * + * @return string + * @see PEAR2\Console\CommandLine_MessageProvider_Default + */ + public function get($code, $vars=array()); + + // }}} +} diff --git a/system/autoload/PEAR2/Console/CommandLine/MessageProvider/Default.php b/system/autoload/PEAR2/Console/CommandLine/MessageProvider/Default.php new file mode 100644 index 0000000..c3781f7 --- /dev/null +++ b/system/autoload/PEAR2/Console/CommandLine/MessageProvider/Default.php @@ -0,0 +1,143 @@ + + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 0.2.1 + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since File available since release 0.1.0 + * @filesource + */ + +namespace PEAR2\Console\CommandLine; + +/** + * Lightweight class that manages messages used by PEAR2\Console\CommandLine package, + * allowing the developper to customize these messages, for example to + * internationalize a command line frontend. + * + * @category Console + * @package PEAR2\Console\CommandLine + * @author David JEAN LOUIS + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since Class available since release 0.1.0 + */ +class MessageProvider_Default + implements MessageProvider, + CustomMessageProvider +{ + // Properties {{{ + + /** + * Associative array of messages + * + * @var array $messages + */ + protected $messages = array( + 'OPTION_VALUE_REQUIRED' => 'Option "{$name}" requires a value.', + 'OPTION_VALUE_UNEXPECTED' => 'Option "{$name}" does not expect a value (got "{$value}").', + 'OPTION_VALUE_NOT_VALID' => 'Option "{$name}" must be one of the following: "{$choices}" (got "{$value}").', + 'OPTION_VALUE_TYPE_ERROR' => 'Option "{$name}" requires a value of type {$type} (got "{$value}").', + 'OPTION_AMBIGUOUS' => 'Ambiguous option "{$name}", can be one of the following: {$matches}.', + 'OPTION_UNKNOWN' => 'Unknown option "{$name}".', + 'ARGUMENT_REQUIRED' => 'You must provide at least {$argnum} argument{$plural}.', + 'PROG_HELP_LINE' => 'Type "{$progname} --help" to get help.', + 'PROG_VERSION_LINE' => '{$progname} version {$version}.', + 'COMMAND_HELP_LINE' => 'Type "{$progname} --help" to get help on specific command.', + 'USAGE_WORD' => 'Usage', + 'OPTION_WORD' => 'Options', + 'ARGUMENT_WORD' => 'Arguments', + 'COMMAND_WORD' => 'Commands', + 'PASSWORD_PROMPT' => 'Password: ', + 'PASSWORD_PROMPT_ECHO' => 'Password (warning: will echo): ', + 'INVALID_CUSTOM_INSTANCE' => 'Instance does not implement the required interface', + 'LIST_OPTION_MESSAGE' => 'lists valid choices for option {$name}', + 'LIST_DISPLAYED_MESSAGE' => 'Valid choices are: ', + 'INVALID_SUBCOMMAND' => 'Command "{$command}" is not valid.', + 'SUBCOMMAND_REQUIRED' => 'Please enter one of the following command: {$commands}.', + ); + + // }}} + // get() {{{ + + /** + * Retrieve the given string identifier corresponding message. + * + * @param string $code The string identifier of the message + * @param array $vars An array of template variables + * + * @return string + */ + public function get($code, $vars = array()) + { + if (!isset($this->messages[$code])) { + return 'UNKNOWN'; + } + return $this->replaceTemplateVars($this->messages[$code], $vars); + } + + // }}} + // getWithCustomMessages() {{{ + + /** + * Retrieve the given string identifier corresponding message. + * + * @param string $code The string identifier of the message + * @param array $vars An array of template variables + * @param array $messages An optional array of messages to use. Array + * indexes are message codes. + * + * @return string + */ + public function getWithCustomMessages( + $code, $vars = array(), $messages = array() + ) { + // get message + if (isset($messages[$code])) { + $message = $messages[$code]; + } elseif (isset($this->messages[$code])) { + $message = $this->messages[$code]; + } else { + $message = 'UNKNOWN'; + } + return $this->replaceTemplateVars($message, $vars); + } + + // }}} + // replaceTemplateVars() {{{ + + /** + * Replaces template vars in a message + * + * @param string $message The message + * @param array $vars An array of template variables + * + * @return string + */ + protected function replaceTemplateVars($message, $vars = array()) + { + $tmpkeys = array_keys($vars); + $keys = array(); + foreach ($tmpkeys as $key) { + $keys[] = '{$' . $key . '}'; + } + return str_replace($keys, array_values($vars), $message); + } + + // }}} +} diff --git a/system/autoload/PEAR2/Console/CommandLine/Option.php b/system/autoload/PEAR2/Console/CommandLine/Option.php new file mode 100644 index 0000000..912e8aa --- /dev/null +++ b/system/autoload/PEAR2/Console/CommandLine/Option.php @@ -0,0 +1,393 @@ + + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 0.2.1 + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since File available since release 0.1.0 + * @filesource + */ + +namespace PEAR2\Console\CommandLine; + +use PEAR2\Console; + +/** + * Class that represent a commandline option. + * + * @category Console + * @package PEAR2\Console\CommandLine + * @author David JEAN LOUIS + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since Class available since release 0.1.0 + */ +class Option extends Element +{ + // Public properties {{{ + + /** + * The option short name (ex: -v). + * + * @var string $short_name Short name of the option + */ + public $short_name; + + /** + * The option long name (ex: --verbose). + * + * @var string $long_name Long name of the option + */ + public $long_name; + + /** + * The option action, defaults to "StoreString". + * + * @var string $action Option action + */ + public $action = 'StoreString'; + + /** + * An array of possible values for the option. If this array is not empty + * and the value passed is not in the array an exception is raised. + * This only make sense for actions that accept values of course. + * + * @var array $choices Valid choices for the option + */ + public $choices = array(); + + /** + * The callback function (or method) to call for an action of type + * Callback, this can be any callable supported by the php function + * call_user_func. + * + * Example: + * + * + * $parser->addOption('myoption', array( + * 'short_name' => '-m', + * 'long_name' => '--myoption', + * 'action' => 'Callback', + * 'callback' => 'myCallbackFunction' + * )); + * + * + * @var callable $callback The option callback + */ + public $callback; + + /** + * An associative array of additional params to pass to the class + * corresponding to the action, this array will also be passed to the + * callback defined for an action of type Callback, Example: + * + * + * // for a custom action + * $parser->addOption('myoption', array( + * 'short_name' => '-m', + * 'long_name' => '--myoption', + * 'action' => 'MyCustomAction', + * 'action_params' => array('foo'=>true, 'bar'=>false) + * )); + * + * // if the user type: + * // $ -m spam + * // in your MyCustomAction class the execute() method will be called + * // with the value 'spam' as first parameter and + * // array('foo'=>true, 'bar'=>false) as second parameter + * + * + * @var array $action_params Additional parameters to pass to the action + */ + public $action_params = array(); + + /** + * For options that expect an argument, this property tells the parser if + * the option argument is optional and can be ommited. + * + * @var bool $argumentOptional Whether the option arg is optional or not + */ + public $argument_optional = false; + + /** + * For options that uses the "choice" property only. + * Adds a --list- option to the parser that displays the list of + * choices for the option. + * + * @var bool $add_list_option Whether to add a list option or not + */ + public $add_list_option = false; + + // }}} + // Private properties {{{ + + /** + * When an action is called remember it to allow for multiple calls. + * + * @var object $action_instance Placeholder for action + */ + private $_action_instance = null; + + // }}} + // __construct() {{{ + + /** + * Constructor. + * + * @param string $name The name of the option + * @param array $params An optional array of parameters + * + * @return void + */ + public function __construct($name = null, $params = array()) + { + parent::__construct($name, $params); + if ($this->action == 'Password') { + // special case for Password action, password can be passed to the + // commandline or prompted by the parser + $this->argument_optional = true; + } + } + + // }}} + // toString() {{{ + + /** + * Returns the string representation of the option. + * + * @param string $delim Delimiter to use between short and long option + * + * @return string The string representation of the option + * @todo use __toString() instead + */ + public function toString($delim = ", ") + { + $ret = ''; + $padding = ''; + if ($this->short_name != null) { + $ret .= $this->short_name; + if ($this->expectsArgument()) { + $ret .= ' ' . $this->help_name; + } + $padding = $delim; + } + if ($this->long_name != null) { + $ret .= $padding . $this->long_name; + if ($this->expectsArgument()) { + $ret .= '=' . $this->help_name; + } + } + return $ret; + } + + // }}} + // expectsArgument() {{{ + + /** + * Returns true if the option requires one or more argument and false + * otherwise. + * + * @return bool Whether the option expects an argument or not + */ + public function expectsArgument() + { + if ($this->action == 'StoreTrue' + || $this->action == 'StoreFalse' + || $this->action == 'Help' + || $this->action == 'Version' + || $this->action == 'Counter' + || $this->action == 'List' + ) { + return false; + } + return true; + } + + // }}} + // dispatchAction() {{{ + + /** + * Formats the value $value according to the action of the option and + * updates the passed PEAR2\Console\CommandLine_Result object. + * + * @param mixed $value The value to format + * @param PEAR2\Console\CommandLine_Result $result The result instance + * @param PEAR2\Console\CommandLine $parser The parser instance + * + * @return void + * @throws PEAR2\Console\CommandLine\Exception + */ + public function dispatchAction($value, $result, $parser) + { + $actionInfo = Console\CommandLine::$actions[$this->action]; + $clsname = $actionInfo[0]; + if ($this->_action_instance === null) { + $this->_action_instance = new $clsname($result, $this, $parser); + } + + // check value is in option choices + if (!empty($this->choices) + && !in_array( + $this->_action_instance->format($value), + $this->choices + ) + ) { + throw Console\CommandLine\Exception::factory( + 'OPTION_VALUE_NOT_VALID', + array( + 'name' => $this->name, + 'choices' => implode('", "', $this->choices), + 'value' => $value, + ), + $parser, + $this->messages + ); + } + $this->_action_instance->execute($value, $this->action_params); + } + + // }}} + // validate() {{{ + + /** + * Validates the option instance. + * + * @return void + * @throws PEAR2\Console\CommandLine\Exception + * @todo use exceptions instead + */ + public function validate() + { + // check if the option name is valid + if (!preg_match( + '/^[a-zA-Z_\x7f-\xff]+[a-zA-Z0-9_\x7f-\xff]*$/', + $this->name + ) + ) { + Console\CommandLine::triggerError( + 'option_bad_name', + E_USER_ERROR, + array('{$name}' => $this->name) + ); + } + // call the parent validate method + parent::validate(); + // a short_name or a long_name must be provided + if ($this->short_name == null && $this->long_name == null) { + Console\CommandLine::triggerError( + 'option_long_and_short_name_missing', + E_USER_ERROR, + array('{$name}' => $this->name) + ); + } + // check if the option short_name is valid + if ($this->short_name != null + && !(preg_match('/^\-[a-zA-Z]{1}$/', $this->short_name)) + ) { + Console\CommandLine::triggerError( + 'option_bad_short_name', + E_USER_ERROR, + array( + '{$name}' => $this->name, + '{$short_name}' => $this->short_name + ) + ); + } + // check if the option long_name is valid + if ($this->long_name != null + && !preg_match('/^\-\-[a-zA-Z]+[a-zA-Z0-9_\-]*$/', $this->long_name) + ) { + Console\CommandLine::triggerError( + 'option_bad_long_name', + E_USER_ERROR, + array( + '{$name}' => $this->name, + '{$long_name}' => $this->long_name + ) + ); + } + // check if we have a valid action + if (!is_string($this->action)) { + Console\CommandLine::triggerError( + 'option_bad_action', + E_USER_ERROR, + array('{$name}' => $this->name) + ); + } + if (!isset(Console\CommandLine::$actions[$this->action])) { + Console\CommandLine::triggerError( + 'option_unregistered_action', + E_USER_ERROR, + array( + '{$action}' => $this->action, + '{$name}' => $this->name + ) + ); + } + // if the action is a callback, check that we have a valid callback + if ($this->action == 'Callback' && !is_callable($this->callback)) { + Console\CommandLine::triggerError( + 'option_invalid_callback', + E_USER_ERROR, + array('{$name}' => $this->name) + ); + } + } + + // }}} + // setDefaults() {{{ + + /** + * Set the default value according to the configured action. + * + * Note that for backward compatibility issues this method is only called + * when the 'force_options_defaults' is set to true, it will become the + * default behaviour in the next major release of PEAR2\Console\CommandLine. + * + * @return void + */ + public function setDefaults() + { + if ($this->default !== null) { + // already set + return; + } + switch ($this->action) { + case 'Counter': + case 'StoreInt': + $this->default = 0; + break; + case 'StoreFloat': + $this->default = 0.0; + break; + case 'StoreArray': + $this->default = array(); + break; + case 'StoreTrue': + $this->default = false; + break; + case 'StoreFalse': + $this->default = true; + break; + default: + return; + } + } + + // }}} +} diff --git a/system/autoload/PEAR2/Console/CommandLine/Outputter.php b/system/autoload/PEAR2/Console/CommandLine/Outputter.php new file mode 100644 index 0000000..ecb0526 --- /dev/null +++ b/system/autoload/PEAR2/Console/CommandLine/Outputter.php @@ -0,0 +1,64 @@ + + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 0.2.1 + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since File available since release 0.1.0 + * @filesource + */ + +namespace PEAR2\Console\CommandLine; + +/** + * Outputters common interface, all outputters must implement this interface. + * + * @category Console + * @package PEAR2\Console\CommandLine + * @author David JEAN LOUIS + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since Class available since release 0.1.0 + */ +interface Outputter +{ + // stdout() {{{ + + /** + * Processes the output for a message that should be displayed on STDOUT. + * + * @param string $msg The message to output + * + * @return void + */ + public function stdout($msg); + + // }}} + // stderr() {{{ + + /** + * Processes the output for a message that should be displayed on STDERR. + * + * @param string $msg The message to output + * + * @return void + */ + public function stderr($msg); + + // }}} +} diff --git a/system/autoload/PEAR2/Console/CommandLine/Outputter/Default.php b/system/autoload/PEAR2/Console/CommandLine/Outputter/Default.php new file mode 100644 index 0000000..9b4d0c3 --- /dev/null +++ b/system/autoload/PEAR2/Console/CommandLine/Outputter/Default.php @@ -0,0 +1,78 @@ + + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 0.2.1 + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since File available since release 0.1.0 + * @filesource + */ + +namespace PEAR2\Console\CommandLine; + +/** + * PEAR2\Console\CommandLine default Outputter. + * + * @category Console + * @package PEAR2\Console\CommandLine + * @author David JEAN LOUIS + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since Class available since release 0.1.0 + */ +class Outputter_Default implements Outputter +{ + // stdout() {{{ + + /** + * Writes the message $msg to STDOUT. + * + * @param string $msg The message to output + * + * @return void + */ + public function stdout($msg) + { + if (defined('STDOUT')) { + fwrite(STDOUT, $msg); + } else { + echo $msg; + } + } + + // }}} + // stderr() {{{ + + /** + * Writes the message $msg to STDERR. + * + * @param string $msg The message to output + * + * @return void + */ + public function stderr($msg) + { + if (defined('STDERR')) { + fwrite(STDERR, $msg); + } else { + echo $msg; + } + } + + // }}} +} diff --git a/system/autoload/PEAR2/Console/CommandLine/Renderer.php b/system/autoload/PEAR2/Console/CommandLine/Renderer.php new file mode 100644 index 0000000..35d175a --- /dev/null +++ b/system/autoload/PEAR2/Console/CommandLine/Renderer.php @@ -0,0 +1,72 @@ + + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 0.2.1 + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since File available since release 0.1.0 + * @filesource + */ + +namespace PEAR2\Console\CommandLine; + +/** + * Renderers common interface, all renderers must implement this interface. + * + * @category Console + * @package PEAR2\Console\CommandLine + * @author David JEAN LOUIS + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since Class available since release 0.1.0 + */ +interface Renderer +{ + // usage() {{{ + + /** + * Returns the full usage message. + * + * @return string The usage message + */ + public function usage(); + + // }}} + // error() {{{ + + /** + * Returns a formatted error message. + * + * @param string $error The error message to format + * + * @return string The error string + */ + public function error($error); + + // }}} + // version() {{{ + + /** + * Returns the program version string. + * + * @return string The version string + */ + public function version(); + + // }}} +} diff --git a/system/autoload/PEAR2/Console/CommandLine/Renderer/Default.php b/system/autoload/PEAR2/Console/CommandLine/Renderer/Default.php new file mode 100644 index 0000000..ff410a3 --- /dev/null +++ b/system/autoload/PEAR2/Console/CommandLine/Renderer/Default.php @@ -0,0 +1,441 @@ + + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 0.2.1 + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since File available since release 0.1.0 + */ + +namespace PEAR2\Console\CommandLine; + +/** + * PEAR2\Console\CommandLine default renderer. + * + * @category Console + * @package PEAR2\Console\CommandLine + * @author David JEAN LOUIS + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since Class available since release 0.1.0 + */ +class Renderer_Default implements Renderer +{ + // Properties {{{ + + /** + * Integer that define the max width of the help text. + * + * @var integer $line_width Line width + */ + public $line_width = 75; + + /** + * Integer that define the max width of the help text. + * + * @var integer $line_width Line width + */ + public $options_on_different_lines = false; + + /** + * An instance of PEAR2\Console\CommandLine. + * + * @var PEAR2\Console\CommandLine $parser The parser + */ + public $parser = false; + + // }}} + // __construct() {{{ + + /** + * Constructor. + * + * @param object $parser A PEAR2\Console\CommandLine instance + * + * @return void + */ + public function __construct($parser = false) + { + $this->parser = $parser; + } + + // }}} + // usage() {{{ + + /** + * Returns the full usage message. + * + * @return string The usage message + */ + public function usage() + { + $ret = ''; + if (!empty($this->parser->description)) { + $ret .= $this->description() . "\n\n"; + } + $ret .= $this->usageLine() . "\n"; + if (count($this->parser->commands) > 0) { + $ret .= $this->commandUsageLine() . "\n"; + } + if (count($this->parser->options) > 0) { + $ret .= "\n" . $this->optionList() . "\n"; + } + if (count($this->parser->args) > 0) { + $ret .= "\n" . $this->argumentList() . "\n"; + } + if (count($this->parser->commands) > 0) { + $ret .= "\n" . $this->commandList() . "\n"; + } + $ret .= "\n"; + return $ret; + } + // }}} + // error() {{{ + + /** + * Returns a formatted error message. + * + * @param string $error The error message to format + * + * @return string The error string + */ + public function error($error) + { + $ret = 'Error: ' . $error . "\n"; + if ($this->parser->add_help_option) { + $name = $this->name(); + $ret .= $this->wrap( + $this->parser->message_provider->get( + 'PROG_HELP_LINE', + array('progname' => $name) + ) + ) . "\n"; + if (count($this->parser->commands) > 0) { + $ret .= $this->wrap( + $this->parser->message_provider->get( + 'COMMAND_HELP_LINE', + array('progname' => $name) + ) + ) . "\n"; + } + } + return $ret; + } + + // }}} + // version() {{{ + + /** + * Returns the program version string. + * + * @return string The version string + */ + public function version() + { + return $this->parser->message_provider->get( + 'PROG_VERSION_LINE', + array( + 'progname' => $this->name(), + 'version' => $this->parser->version + ) + ) . "\n"; + } + + // }}} + // name() {{{ + + /** + * Returns the full name of the program or the sub command + * + * @return string The name of the program + */ + protected function name() + { + $name = $this->parser->name; + $parent = $this->parser->parent; + while ($parent) { + if (count($parent->options) > 0) { + $name = '[' + . strtolower( + $this->parser->message_provider->get( + 'OPTION_WORD', + array('plural' => 's') + ) + ) . '] ' . $name; + } + $name = $parent->name . ' ' . $name; + $parent = $parent->parent; + } + return $this->wrap($name); + } + + // }}} + // description() {{{ + + /** + * Returns the command line description message. + * + * @return string The description message + */ + protected function description() + { + return $this->wrap($this->parser->description); + } + + // }}} + // usageLine() {{{ + + /** + * Returns the command line usage message + * + * @return string the usage message + */ + protected function usageLine() + { + $usage = $this->parser->message_provider->get('USAGE_WORD') . ":\n"; + $ret = $usage . ' ' . $this->name(); + if (count($this->parser->options) > 0) { + $ret .= ' [' + . strtolower($this->parser->message_provider->get('OPTION_WORD')) + . ']'; + } + if (count($this->parser->args) > 0) { + foreach ($this->parser->args as $name=>$arg) { + $arg_str = $arg->help_name; + if ($arg->multiple) { + $arg_str .= '1 ' . $arg->help_name . '2 ...'; + } + if ($arg->optional) { + $arg_str = '[' . $arg_str . ']'; + } + $ret .= ' ' . $arg_str; + } + } + return $this->columnWrap($ret, 2); + } + + // }}} + // commandUsageLine() {{{ + + /** + * Returns the command line usage message for subcommands. + * + * @return string The usage line + */ + protected function commandUsageLine() + { + if (count($this->parser->commands) == 0) { + return ''; + } + $ret = ' ' . $this->name(); + if (count($this->parser->options) > 0) { + $ret .= ' [' + . strtolower($this->parser->message_provider->get('OPTION_WORD')) + . ']'; + } + $ret .= " "; + $hasArgs = false; + $hasOptions = false; + foreach ($this->parser->commands as $command) { + if (!$hasArgs && count($command->args) > 0) { + $hasArgs = true; + } + if (!$hasOptions && ($command->add_help_option + || $command->add_version_option + || count($command->options) > 0) + ) { + $hasOptions = true; + } + } + if ($hasOptions) { + $ret .= ' [options]'; + } + if ($hasArgs) { + $ret .= ' [args]'; + } + return $this->columnWrap($ret, 2); + } + + // }}} + // argumentList() {{{ + + /** + * Render the arguments list that will be displayed to the user, you can + * override this method if you want to change the look of the list. + * + * @return string The formatted argument list + */ + protected function argumentList() + { + $col = 0; + $args = array(); + foreach ($this->parser->args as $arg) { + $argstr = ' ' . $arg->toString(); + $args[] = array($argstr, $arg->description); + $ln = strlen($argstr); + if ($col < $ln) { + $col = $ln; + } + } + $ret = $this->parser->message_provider->get('ARGUMENT_WORD') . ":"; + foreach ($args as $arg) { + $text = str_pad($arg[0], $col) . ' ' . $arg[1]; + $ret .= "\n" . $this->columnWrap($text, $col+2); + } + return $ret; + } + + // }}} + // optionList() {{{ + + /** + * Render the options list that will be displayed to the user, you can + * override this method if you want to change the look of the list. + * + * @return string The formatted option list + */ + protected function optionList() + { + $col = 0; + $options = array(); + foreach ($this->parser->options as $option) { + $delim = $this->options_on_different_lines ? "\n" : ', '; + $optstr = $option->toString($delim); + $lines = explode("\n", $optstr); + $lines[0] = ' ' . $lines[0]; + if (count($lines) > 1) { + $lines[1] = ' ' . $lines[1]; + $ln = strlen($lines[1]); + } else { + $ln = strlen($lines[0]); + } + $options[] = array($lines, $option->description); + if ($col < $ln) { + $col = $ln; + } + } + $ret = $this->parser->message_provider->get('OPTION_WORD') . ":"; + foreach ($options as $option) { + if (count($option[0]) > 1) { + $text = str_pad($option[0][1], $col) . ' ' . $option[1]; + $pre = $option[0][0] . "\n"; + } else { + $text = str_pad($option[0][0], $col) . ' ' . $option[1]; + $pre = ''; + } + $ret .= "\n" . $pre . $this->columnWrap($text, $col+2); + } + return $ret; + } + + // }}} + // commandList() {{{ + + /** + * Render the command list that will be displayed to the user, you can + * override this method if you want to change the look of the list. + * + * @return string The formatted subcommand list + */ + protected function commandList() + { + + $commands = array(); + $col = 0; + foreach ($this->parser->commands as $cmdname=>$command) { + $cmdname = ' ' . $cmdname; + $commands[] = array($cmdname, $command->description, $command->aliases); + $ln = strlen($cmdname); + if ($col < $ln) { + $col = $ln; + } + } + $ret = $this->parser->message_provider->get('COMMAND_WORD') . ":"; + foreach ($commands as $command) { + $text = str_pad($command[0], $col) . ' ' . $command[1]; + if ($aliasesCount = count($command[2])) { + $pad = ''; + $text .= ' ('; + $text .= $aliasesCount > 1 ? 'aliases: ' : 'alias: '; + foreach ($command[2] as $alias) { + $text .= $pad . $alias; + $pad = ', '; + } + $text .= ')'; + } + $ret .= "\n" . $this->columnWrap($text, $col+2); + } + return $ret; + } + + // }}} + // wrap() {{{ + + /** + * Wraps the text passed to the method. + * + * @param string $text The text to wrap + * @param int $lw The column width (defaults to line_width property) + * + * @return string The wrapped text + */ + protected function wrap($text, $lw=null) + { + if ($this->line_width > 0) { + if ($lw === null) { + $lw = $this->line_width; + } + return wordwrap($text, $lw, "\n", false); + } + return $text; + } + + // }}} + // columnWrap() {{{ + + /** + * Wraps the text passed to the method at the specified width. + * + * @param string $text The text to wrap + * @param int $cw The wrap width + * + * @return string The wrapped text + */ + protected function columnWrap($text, $cw) + { + $tokens = explode("\n", $this->wrap($text)); + $ret = $tokens[0]; + $text = trim(substr($text, strlen($ret))); + if (empty($text)) { + return $ret; + } + + $chunks = $this->wrap($text, $this->line_width - $cw); + $tokens = explode("\n", $chunks); + foreach ($tokens as $token) { + if (!empty($token)) { + $ret .= "\n" . str_repeat(' ', $cw) . $token; + } else { + $ret .= "\n"; + } + } + return $ret; + } + + // }}} +} diff --git a/system/autoload/PEAR2/Console/CommandLine/Result.php b/system/autoload/PEAR2/Console/CommandLine/Result.php new file mode 100644 index 0000000..20e7e8c --- /dev/null +++ b/system/autoload/PEAR2/Console/CommandLine/Result.php @@ -0,0 +1,72 @@ + + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 0.2.1 + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since File available since release 0.1.0 + * @filesource + */ + +namespace PEAR2\Console\CommandLine; + +/** + * A lightweight class to store the result of the command line parsing. + * + * @category Console + * @package PEAR2\Console\CommandLine + * @author David JEAN LOUIS + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since Class available since release 0.1.0 + */ +class Result +{ + // Public properties {{{ + + /** + * The result options associative array. + * Key is the name of the option and value its value. + * + * @var array $options Result options array + */ + public $options = array(); + + /** + * The result arguments array. + * + * @var array $args Result arguments array + */ + public $args = array(); + + /** + * Name of the command invoked by the user, false if no command invoked. + * + * @var string $command_name Result command name + */ + public $command_name = false; + + /** + * A result instance for the subcommand. + * + * @var static $command Result instance for the subcommand + */ + public $command = false; + + // }}} +} diff --git a/system/autoload/PEAR2/Console/CommandLine/XmlParser.php b/system/autoload/PEAR2/Console/CommandLine/XmlParser.php new file mode 100644 index 0000000..bddd4df --- /dev/null +++ b/system/autoload/PEAR2/Console/CommandLine/XmlParser.php @@ -0,0 +1,303 @@ + + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @version 0.2.1 + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since File available since release 0.1.0 + * @filesource + */ + +namespace PEAR2\Console\CommandLine; + +use PEAR2\Console\CommandLine; + +/** + * Parser for command line xml definitions. + * + * @category Console + * @package PEAR2\Console\CommandLine + * @author David JEAN LOUIS + * @copyright 2007-2009 David JEAN LOUIS + * @license http://opensource.org/licenses/mit-license.php MIT License + * @link http://pear2.php.net/PEAR2_Console_CommandLine + * @since Class available since release 0.1.0 + */ +class XmlParser +{ + // parse() {{{ + + /** + * Parses the given xml definition file and returns a + * PEAR2\Console\CommandLine instance constructed with the xml data. + * + * @param string $xmlfile The xml file to parse + * + * @return PEAR2\Console\CommandLine A parser instance + */ + public static function parse($xmlfile) + { + if (!is_readable($xmlfile)) { + CommandLine::triggerError( + 'invalid_xml_file', + E_USER_ERROR, + array('{$file}' => $xmlfile) + ); + } + $doc = new \DomDocument(); + $doc->load($xmlfile); + self::validate($doc); + $nodes = $doc->getElementsByTagName('command'); + $root = $nodes->item(0); + return self::_parseCommandNode($root, true); + } + + // }}} + // parseString() {{{ + + /** + * Parses the given xml definition string and returns a + * PEAR2\Console\CommandLine instance constructed with the xml data. + * + * @param string $xmlstr The xml string to parse + * + * @return PEAR2\Console\CommandLine A parser instance + */ + public static function parseString($xmlstr) + { + $doc = new \DomDocument(); + $doc->loadXml($xmlstr); + self::validate($doc); + $nodes = $doc->getElementsByTagName('command'); + $root = $nodes->item(0); + return self::_parseCommandNode($root, true); + } + + // }}} + // validate() {{{ + + /** + * Validates the xml definition using Relax NG. + * + * @param DomDocument $doc The document to validate + * + * @return boolean Whether the xml data is valid or not. + * @throws PEAR2\Console\CommandLine\Exception + * @todo use exceptions only + */ + public static function validate($doc) + { + $rngfile = __DIR__ + . '/../../../../data/pear2.php.net/PEAR2_Console_CommandLine/xmlschema.rng'; + if (!is_file($rngfile)) { + $rngfile = __DIR__ . '/../../../../data/xmlschema.rng'; + } + if (!is_readable($rngfile)) { + CommandLine::triggerError( + 'invalid_xml_file', + E_USER_ERROR, + array('{$file}' => $rngfile) + ); + } + return $doc->relaxNGValidate($rngfile); + } + + // }}} + // _parseCommandNode() {{{ + + /** + * Parses the root command node or a command node and returns the + * constructed PEAR2\Console\CommandLine or PEAR2\Console\CommandLine_Command + * instance. + * + * @param DomDocumentNode $node The node to parse + * @param bool $isRootNode Whether it is a root node or not + * + * @return mixed PEAR2\Console\CommandLine or PEAR2\Console\CommandLine_Command + */ + private static function _parseCommandNode($node, $isRootNode = false) + { + if ($isRootNode) { + $obj = new CommandLine(); + } else { + $obj = new CommandLine\Command(); + } + foreach ($node->childNodes as $cNode) { + $cNodeName = $cNode->nodeName; + switch ($cNodeName) { + case 'name': + case 'description': + case 'version': + $obj->$cNodeName = trim($cNode->nodeValue); + break; + case 'add_help_option': + case 'add_version_option': + case 'force_posix': + $obj->$cNodeName = self::_bool(trim($cNode->nodeValue)); + break; + case 'option': + $obj->addOption(self::_parseOptionNode($cNode)); + break; + case 'argument': + $obj->addArgument(self::_parseArgumentNode($cNode)); + break; + case 'command': + $obj->addCommand(self::_parseCommandNode($cNode)); + break; + case 'aliases': + if (!$isRootNode) { + foreach ($cNode->childNodes as $subChildNode) { + if ($subChildNode->nodeName == 'alias') { + $obj->aliases[] = trim($subChildNode->nodeValue); + } + } + } + break; + case 'messages': + $obj->messages = self::_messages($cNode); + break; + default: + break; + } + } + return $obj; + } + + // }}} + // _parseOptionNode() {{{ + + /** + * Parses an option node and returns the constructed + * PEAR2\Console\CommandLine_Option instance. + * + * @param DomDocumentNode $node The node to parse + * + * @return PEAR2\Console\CommandLine\Option The built option + */ + private static function _parseOptionNode($node) + { + $obj = new CommandLine\Option($node->getAttribute('name')); + foreach ($node->childNodes as $cNode) { + $cNodeName = $cNode->nodeName; + switch ($cNodeName) { + case 'choices': + foreach ($cNode->childNodes as $subChildNode) { + if ($subChildNode->nodeName == 'choice') { + $obj->choices[] = trim($subChildNode->nodeValue); + } + } + break; + case 'messages': + $obj->messages = self::_messages($cNode); + break; + default: + if (property_exists($obj, $cNodeName)) { + $obj->$cNodeName = trim($cNode->nodeValue); + } + break; + } + } + if ($obj->action == 'Password') { + $obj->argument_optional = true; + } + return $obj; + } + + // }}} + // _parseArgumentNode() {{{ + + /** + * Parses an argument node and returns the constructed + * PEAR2\Console\CommandLine_Argument instance. + * + * @param DomDocumentNode $node The node to parse + * + * @return PEAR2\Console\CommandLine\Argument The built argument + */ + private static function _parseArgumentNode($node) + { + $obj = new CommandLine\Argument($node->getAttribute('name')); + foreach ($node->childNodes as $cNode) { + $cNodeName = $cNode->nodeName; + switch ($cNodeName) { + case 'description': + case 'help_name': + case 'default': + $obj->$cNodeName = trim($cNode->nodeValue); + break; + case 'multiple': + $obj->multiple = self::_bool(trim($cNode->nodeValue)); + break; + case 'optional': + $obj->optional = self::_bool(trim($cNode->nodeValue)); + break; + case 'messages': + $obj->messages = self::_messages($cNode); + break; + default: + break; + } + } + return $obj; + } + + // }}} + // _bool() {{{ + + /** + * Returns a boolean according to true/false possible strings. + * + * @param string $str The string to process + * + * @return boolean + */ + private static function _bool($str) + { + return in_array((string)$str, array('true', '1', 'on', 'yes')); + } + + // }}} + // _messages() {{{ + + /** + * Returns an array of custom messages for the element + * + * @param DOMNode $node The messages node to process + * + * @return array an array of messages + * + * @see PEAR2\Console\CommandLine::$messages + * @see PEAR2\Console\CommandLine_Element::$messages + */ + private static function _messages(DOMNode $node) + { + $messages = array(); + + foreach ($node->childNodes as $cNode) { + if ($cNode->nodeType == XML_ELEMENT_NODE) { + $name = $cNode->getAttribute('name'); + $value = trim($cNode->nodeValue); + + $messages[$name] = $value; + } + } + + return $messages; + } + + // }}} +} diff --git a/system/autoload/PEAR2/Net/RouterOS/Client.php b/system/autoload/PEAR2/Net/RouterOS/Client.php new file mode 100644 index 0000000..88b13e5 --- /dev/null +++ b/system/autoload/PEAR2/Net/RouterOS/Client.php @@ -0,0 +1,819 @@ + + * @copyright 2011 Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 1.0.0b5 + * @link http://pear2.php.net/PEAR2_Net_RouterOS + */ +/** + * The namespace declaration. + */ +namespace PEAR2\Net\RouterOS; + +/** + * Refers to transmitter direction constants. + */ +use PEAR2\Net\Transmitter\Stream as S; + +/** + * Refers to the cryptography constants. + */ +use PEAR2\Net\Transmitter\NetworkStream as N; + +/** + * Catches arbitrary exceptions at some points. + */ +use Exception as E; + +/** + * A RouterOS client. + * + * Provides functionality for easily communicating with a RouterOS host. + * + * @category Net + * @package PEAR2_Net_RouterOS + * @author Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Net_RouterOS + */ +class Client +{ + /** + * Used in {@link static::isRequestActive()} to limit search only to + * requests that have a callback. + */ + const FILTER_CALLBACK = 1; + /** + * Used in {@link static::isRequestActive()} to limit search only to + * requests that use the buffer. + */ + const FILTER_BUFFER = 2; + /** + * Used in {@link static::isRequestActive()} to indicate no limit in search. + */ + const FILTER_ALL = 3; + + /** + * @var Communicator The communicator for this client. + */ + protected $com; + + /** + * @var int The number of currently pending requests. + */ + protected $pendingRequestsCount = 0; + + /** + * @var array An array of responses that have not yet been extracted or + * passed to a callback. Key is the tag of the request, and the value + * is an array of associated responses. + */ + protected $responseBuffer = array(); + + /** + * @var array An array of callbacks to be executed as responses come. + * Key is the tag of the request, and the value is the callback for it. + */ + protected $callbacks = array(); + + /** + * @var Registry A registry for the operations. Particularly helpful at + * persistent connections. + */ + protected $registry = null; + + /** + * @var bool Whether to stream future responses. + */ + private $_streamingResponses = false; + + /** + * Creates a new instance of a RouterOS API client. + * + * Creates a new instance of a RouterOS API client with the specified + * settings. + * + * @param string $host Hostname (IP or domain) of the RouterOS server. + * @param string $username The RouterOS username. + * @param string $password The RouterOS password. + * @param int|null $port The port on which the RouterOS server provides + * the API service. You can also specify NULL, in which case the port + * will automatically be chosen between 8728 and 8729, depending on the + * value of $crypto. + * @param bool $persist Whether or not the connection should be a + * persistent one. + * @param float $timeout The timeout for the connection. + * @param string $crypto The encryption for this connection. Must be one + * of the PEAR2\Net\Transmitter\NetworkStream::CRYPTO_* constants. Off + * by default. RouterOS currently supports only TLS, but the setting is + * provided in this fashion for forward compatibility's sake. And for + * the sake of simplicity, if you specify an encryption, don't specify a + * context and your default context uses the value "DEFAULT" for + * ciphers, "ADH" will be automatically added to the list of ciphers. + * @param resource $context A context for the socket. + * + * @see sendSync() + * @see sendAsync() + */ + public function __construct( + $host, + $username, + $password = '', + $port = 8728, + $persist = false, + $timeout = null, + $crypto = N::CRYPTO_OFF, + $context = null + ) { + $this->com = new Communicator( + $host, + $port, + $persist, + $timeout, + $username . '/' . $password, + $crypto, + $context + ); + $timeout = null == $timeout + ? ini_get('default_socket_timeout') + : (int) $timeout; + //Login the user if necessary + if ((!$persist + || !($old = $this->com->getTransmitter()->lock(S::DIRECTION_ALL))) + && $this->com->getTransmitter()->isFresh() + ) { + if (!static::login($this->com, $username, $password, $timeout)) { + $this->com->close(); + throw new DataFlowException( + 'Invalid username or password supplied.', + DataFlowException::CODE_INVALID_CREDENTIALS + ); + } + } + + if (isset($old)) { + $this->com->getTransmitter()->lock($old, true); + } + + if ($persist) { + $this->registry = new Registry("{$host}:{$port}/{$username}"); + } + } + + /** + * A shorthand gateway. + * + * This is a magic PHP method that allows you to call the object as a + * function. Depending on the argument given, one of the other functions in + * the class is invoked and its returned value is returned by this function. + * + * @param mixed $arg Value can be either a {@link Request} to send, which + * would be sent asynchoniously if it has a tag, and synchroniously if + * not, a number to loop with or NULL to complete all pending requests. + * Any other value is converted to string and treated as the tag of a + * request to complete. + * + * @return mixed Whatever the long form function would have returned. + */ + public function __invoke($arg = null) + { + if (is_int($arg) || is_double($arg)) { + return $this->loop($arg); + } elseif ($arg instanceof Request) { + return '' == $arg->getTag() ? $this->sendSync($arg) + : $this->sendAsync($arg); + } elseif (null === $arg) { + return $this->completeRequest(); + } + return $this->completeRequest((string) $arg); + } + + /** + * Login to a RouterOS connection. + * + * @param Communicator $com The communicator to attempt to login to. + * @param string $username The RouterOS username. + * @param string $password The RouterOS password. + * @param int|null $timeout The time to wait for each response. NULL + * waits indefinetly. + * + * @return bool TRUE on success, FALSE on failure. + */ + public static function login( + Communicator $com, + $username, + $password = '', + $timeout = null + ) { + if (null !== ($remoteCharset = $com->getCharset($com::CHARSET_REMOTE)) + && null !== ($localCharset = $com->getCharset($com::CHARSET_LOCAL)) + ) { + $password = iconv( + $localCharset, + $remoteCharset . '//IGNORE//TRANSLIT', + $password + ); + } + $old = null; + try { + if ($com->getTransmitter()->isPersistent()) { + $old = $com->getTransmitter()->lock(S::DIRECTION_ALL); + $result = self::_login($com, $username, $password, $timeout); + $com->getTransmitter()->lock($old, true); + return $result; + } + return self::_login($com, $username, $password, $timeout); + } catch (E $e) { + if ($com->getTransmitter()->isPersistent() && null !== $old) { + $com->getTransmitter()->lock($old, true); + } + throw ($e instanceof NotSupportedException + || $e instanceof UnexpectedValueException + || !$com->getTransmitter()->isDataAwaiting()) ? new SocketException( + 'This is not a compatible RouterOS service', + SocketException::CODE_SERVICE_INCOMPATIBLE, + $e + ) : $e; + } + } + + /** + * Login to a RouterOS connection. + * + * This is the actual login procedure, applied regardless of persistence and + * charset settings. + * + * @param Communicator $com The communicator to attempt to login to. + * @param string $username The RouterOS username. + * @param string $password The RouterOS password. Potentially parsed + * already by iconv. + * @param int|null $timeout The time to wait for each response. NULL + * waits indefinetly. + * + * @return bool TRUE on success, FALSE on failure. + */ + private static function _login( + Communicator $com, + $username, + $password = '', + $timeout = null + ) { + $request = new Request('/login'); + $request->send($com); + $response = new Response($com, false, $timeout); + $request->setArgument('name', $username); + $request->setArgument( + 'response', + '00' . md5( + chr(0) . $password + . pack('H*', $response->getProperty('ret')) + ) + ); + $request->send($com); + $response = new Response($com, false, $timeout); + return $response->getType() === Response::TYPE_FINAL + && null === $response->getProperty('ret'); + } + + /** + * Sets the charset(s) for this connection. + * + * Sets the charset(s) for this connection. The specified charset(s) will be + * used for all future requests and responses. When sending, + * {@link Communicator::CHARSET_LOCAL} is converted to + * {@link Communicator::CHARSET_REMOTE}, and when receiving, + * {@link Communicator::CHARSET_REMOTE} is converted to + * {@link Communicator::CHARSET_LOCAL}. Setting NULL to either charset will + * disable charset convertion, and data will be both sent and received "as + * is". + * + * @param mixed $charset The charset to set. If $charsetType is + * {@link Communicator::CHARSET_ALL}, you can supply either a string to + * use for all charsets, or an array with the charset types as keys, and + * the charsets as values. + * @param int $charsetType Which charset to set. Valid values are the + * Communicator::CHARSET_* constants. Any other value is treated as + * {@link Communicator::CHARSET_ALL}. + * + * @return string|array The old charset. If $charsetType is + * {@link Communicator::CHARSET_ALL}, the old values will be returned as + * an array with the types as keys, and charsets as values. + * @see Communicator::setDefaultCharset() + */ + public function setCharset( + $charset, + $charsetType = Communicator::CHARSET_ALL + ) { + return $this->com->setCharset($charset, $charsetType); + } + + /** + * Gets the charset(s) for this connection. + * + * @param int $charsetType Which charset to get. Valid values are the + * Communicator::CHARSET_* constants. Any other value is treated as + * {@link Communicator::CHARSET_ALL}. + * + * @return string|array The current charset. If $charsetType is + * {@link Communicator::CHARSET_ALL}, the current values will be + * returned as an array with the types as keys, and charsets as values. + * @see setCharset() + */ + public function getCharset($charsetType) + { + return $this->com->getCharset($charsetType); + } + + /** + * Sends a request and waits for responses. + * + * @param Request $request The request to send. + * @param callback $callback Optional. A function that is to be executed + * when new responses for this request are available. The callback takes + * two parameters. The {@link Response} object as the first, and the + * {@link Client} object as the second one. If the function returns + * TRUE, the request is canceled. Note that the callback may be executed + * one last time after that with a response that notifies about the + * canceling. + * + * @return $this The client object. + * @see completeRequest() + * @see loop() + * @see cancelRequest() + */ + public function sendAsync(Request $request, $callback = null) + { + //Error checking + $tag = $request->getTag(); + if ('' == $tag) { + throw new DataFlowException( + 'Asynchonous commands must have a tag.', + DataFlowException::CODE_TAG_REQUIRED + ); + } + if ($this->isRequestActive($tag)) { + throw new DataFlowException( + 'There must not be multiple active requests sharing a tag.', + DataFlowException::CODE_TAG_UNIQUE + ); + } + if (null !== $callback && !is_callable($callback, true)) { + throw new UnexpectedValueException( + 'Invalid callback provided.', + UnexpectedValueException::CODE_CALLBACK_INVALID + ); + } + + $this->send($request); + + if (null === $callback) { + //Register the request at the buffer + $this->responseBuffer[$tag] = array(); + } else { + //Prepare the callback + $this->callbacks[$tag] = $callback; + } + return $this; + } + + /** + * Checks if a request is active. + * + * Checks if a request is active. A request is considered active if it's a + * pending request and/or has responses that are not yet extracted. + * + * @param string $tag The tag of the request to look for. + * @param int $filter One of the FILTER_* consntants. Limits the search + * to the specified places. + * + * @return bool TRUE if the request is active, FALSE otherwise. + * @see getPendingRequestsCount() + * @see completeRequest() + */ + public function isRequestActive($tag, $filter = self::FILTER_ALL) + { + $result = 0; + if ($filter & self::FILTER_CALLBACK) { + $result |= (int) array_key_exists($tag, $this->callbacks); + } + if ($filter & self::FILTER_BUFFER) { + $result |= (int) array_key_exists($tag, $this->responseBuffer); + } + return 0 !== $result; + } + + /** + * Sends a request and gets the full response. + * + * @param Request $request The request to send. + * + * @return ResponseCollection The received responses as a collection. + * @see sendAsync() + * @see close() + */ + public function sendSync(Request $request) + { + $tag = $request->getTag(); + if ('' == $tag) { + $this->send($request); + } else { + $this->sendAsync($request); + } + return $this->completeRequest($tag); + } + + /** + * Completes a specified request. + * + * Starts an event loop for the RouterOS callbacks and finishes when a + * specified request is completed. + * + * @param string $tag The tag of the request to complete. Setting NULL + * completes all requests. + * + * @return ResponseCollection A collection of {@link Response} objects that + * haven't been passed to a callback function or previously extracted + * with {@link static::extractNewResponses()}. Returns an empty + * collection when $tag is set to NULL (responses can still be + * extracted). + */ + public function completeRequest($tag = null) + { + $hasNoTag = '' == $tag; + $result = $hasNoTag ? array() + : $this->extractNewResponses($tag)->toArray(); + while ((!$hasNoTag && $this->isRequestActive($tag)) + || ($hasNoTag && 0 !== $this->getPendingRequestsCount()) + ) { + $newReply = $this->dispatchNextResponse(null); + if ($newReply->getTag() === $tag) { + if ($hasNoTag) { + $result[] = $newReply; + } + if ($newReply->getType() === Response::TYPE_FINAL) { + if (!$hasNoTag) { + $result = array_merge( + $result, + $this->isRequestActive($tag) + ? $this->extractNewResponses($tag)->toArray() + : array() + ); + } + break; + } + } + } + return new ResponseCollection($result); + } + + /** + * Extracts responses for a request. + * + * Gets all new responses for a request that haven't been passed to a + * callback and clears the buffer from them. + * + * @param string $tag The tag of the request to extract new responses for. + * Specifying NULL with extract new responses for all requests. + * + * @return ResponseCollection A collection of {@link Response} objects for + * the specified request. + * @see loop() + */ + public function extractNewResponses($tag = null) + { + if (null === $tag) { + $result = array(); + foreach (array_keys($this->responseBuffer) as $tag) { + $result = array_merge( + $result, + $this->extractNewResponses($tag)->toArray() + ); + } + return new ResponseCollection($result); + } elseif ($this->isRequestActive($tag, self::FILTER_CALLBACK)) { + return new ResponseCollection(array()); + } elseif ($this->isRequestActive($tag, self::FILTER_BUFFER)) { + $result = $this->responseBuffer[$tag]; + if (!empty($result)) { + if (end($result)->getType() === Response::TYPE_FINAL) { + unset($this->responseBuffer[$tag]); + } else { + $this->responseBuffer[$tag] = array(); + } + } + return new ResponseCollection($result); + } else { + throw new DataFlowException( + 'No such request, or the request has already finished.', + DataFlowException::CODE_UNKNOWN_REQUEST + ); + } + } + + /** + * Starts an event loop for the RouterOS callbacks. + * + * Starts an event loop for the RouterOS callbacks and finishes when there + * are no more pending requests or when a specified timeout has passed + * (whichever comes first). + * + * @param int $sTimeout Timeout for the loop. If NULL, there is no time + * limit. + * @param int $usTimeout Microseconds to add to the time limit. + * + * @return bool TRUE when there are any more pending requests, FALSE + * otherwise. + * @see extractNewResponses() + * @see getPendingRequestsCount() + */ + public function loop($sTimeout = null, $usTimeout = 0) + { + try { + if (null === $sTimeout) { + while ($this->getPendingRequestsCount() !== 0) { + $this->dispatchNextResponse(null); + } + } else { + list($usStart, $sStart) = explode(' ', microtime()); + while ($this->getPendingRequestsCount() !== 0 + && ($sTimeout >= 0 || $usTimeout >= 0) + ) { + $this->dispatchNextResponse($sTimeout, $usTimeout); + list($usEnd, $sEnd) = explode(' ', microtime()); + + $sTimeout -= $sEnd - $sStart; + $usTimeout -= $usEnd - $usStart; + if ($usTimeout <= 0) { + if ($sTimeout > 0) { + $usTimeout = 1000000 + $usTimeout; + $sTimeout--; + } + } + + $sStart = $sEnd; + $usStart = $usEnd; + } + } + } catch (SocketException $e) { + if ($e->getCode() !== SocketException::CODE_NO_DATA) { + // @codeCoverageIgnoreStart + // It's impossible to reliably cause any other SocketException. + // This line is only here in case the unthinkable happens: + // The connection terminates just after it was supposedly + // about to send back some data. + throw $e; + // @codeCoverageIgnoreEnd + } + } + return $this->getPendingRequestsCount() !== 0; + } + + /** + * Gets the number of pending requests. + * + * @return int The number of pending requests. + * @see isRequestActive() + */ + public function getPendingRequestsCount() + { + return $this->pendingRequestsCount; + } + + /** + * Cancels a request. + * + * Cancels an active request. Using this function in favor of a plain call + * to the "/cancel" command is highly reccomended, as it also updates the + * counter of pending requests properly. Note that canceling a request also + * removes any responses for it that were not previously extracted with + * {@link static::extractNewResponses()}. + * + * @param string $tag Tag of the request to cancel. Setting NULL will cancel + * all requests. + * + * @return $this The client object. + * @see sendAsync() + * @see close() + */ + public function cancelRequest($tag = null) + { + $cancelRequest = new Request('/cancel'); + $hasTag = !('' == $tag); + $hasReg = null !== $this->registry; + if ($hasReg && !$hasTag) { + $tags = array_merge( + array_keys($this->responseBuffer), + array_keys($this->callbacks) + ); + $this->registry->setTaglessMode(true); + foreach ($tags as $t) { + $cancelRequest->setArgument( + 'tag', + $this->registry->getOwnershipTag() . $t + ); + $this->sendSync($cancelRequest); + } + $this->registry->setTaglessMode(false); + } else { + if ($hasTag) { + if ($this->isRequestActive($tag)) { + if ($hasReg) { + $this->registry->setTaglessMode(true); + $cancelRequest->setArgument( + 'tag', + $this->registry->getOwnershipTag() . $tag + ); + } else { + $cancelRequest->setArgument('tag', $tag); + } + } else { + throw new DataFlowException( + 'No such request. Canceling aborted.', + DataFlowException::CODE_CANCEL_FAIL + ); + } + } + $this->sendSync($cancelRequest); + if ($hasReg) { + $this->registry->setTaglessMode(false); + } + } + + if ($hasTag) { + if ($this->isRequestActive($tag, self::FILTER_BUFFER)) { + $this->responseBuffer[$tag] = $this->completeRequest($tag); + } else { + $this->completeRequest($tag); + } + } else { + $this->loop(); + } + return $this; + } + + /** + * Sets response streaming setting. + * + * Sets whether future responses are streamed. If responses are streamed, + * the argument values are returned as streams instead of strings. This is + * particularly useful if you expect a response that may contain one or more + * very large words. + * + * @param bool $streamingResponses Whether to stream future responses. + * + * @return bool The previous value of the setting. + * @see isStreamingResponses() + */ + public function setStreamingResponses($streamingResponses) + { + $oldValue = $this->_streamingResponses; + $this->_streamingResponses = (bool) $streamingResponses; + return $oldValue; + } + + /** + * Gets response streaming setting. + * + * Gets whether future responses are streamed. + * + * @return bool The value of the setting. + * @see setStreamingResponses() + */ + public function isStreamingResponses() + { + return $this->_streamingResponses; + } + + /** + * Closes the opened connection, even if it is a persistent one. + * + * Closes the opened connection, even if it is a persistent one. Note that + * {@link static::extractNewResponses()} can still be used to extract + * responses collected prior to the closing. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function close() + { + $result = true; + /* + * The check below is done because for some unknown reason + * (either a PHP or a RouterOS bug) calling "/quit" on an encrypted + * connection makes one end hang. + * + * Since encrypted connections only appeared in RouterOS 6.1, and + * the "/quit" call is needed for all <6.0 versions, problems due + * to its absence should be limited to some earlier 6.* versions + * on some RouterBOARD devices. + */ + if ($this->com->getTransmitter()->getCrypto() === N::CRYPTO_OFF) { + if (null !== $this->registry) { + $this->registry->setTaglessMode(true); + } + try { + $response = $this->sendSync(new Request('/quit')); + $result = $response[0]->getType() === Response::TYPE_FATAL; + } catch (SocketException $e) { + $result + = $e->getCode() === SocketException::CODE_REQUEST_SEND_FAIL; + } catch (E $e) { + //Ignore unknown errors. + } + if (null !== $this->registry) { + $this->registry->setTaglessMode(false); + } + } + $result = $result && $this->com->close(); + $this->callbacks = array(); + $this->pendingRequestsCount = 0; + return $result; + } + + /** + * Closes the connection, unless it's a persistent one. + */ + public function __destruct() + { + if ($this->com->getTransmitter()->isPersistent()) { + if (0 !== $this->pendingRequestsCount) { + $this->cancelRequest(); + } + } else { + $this->close(); + } + } + + /** + * Sends a request to RouterOS. + * + * @param Request $request The request to send. + * + * @return $this The client object. + * @see sendSync() + * @see sendAsync() + */ + protected function send(Request $request) + { + $request->send($this->com, $this->registry); + $this->pendingRequestsCount++; + return $this; + } + + /** + * Dispatches the next response in queue. + * + * Dispatches the next response in queue, i.e. it executes the associated + * callback if there is one, or places the response in the response buffer. + * + * @param int $sTimeout If a response is not immediatly available, wait + * this many seconds. If NULL, wait indefinetly. + * @param int $usTimeout Microseconds to add to the waiting time. + * + * @throws SocketException When there's no response within the time limit. + * @return Response The dispatched response. + */ + protected function dispatchNextResponse($sTimeout = 0, $usTimeout = 0) + { + $response = new Response( + $this->com, + $this->_streamingResponses, + $sTimeout, + $usTimeout, + $this->registry + ); + if ($response->getType() === Response::TYPE_FATAL) { + $this->pendingRequestsCount = 0; + $this->com->close(); + return $response; + } + + $tag = $response->getTag(); + $isLastForRequest = $response->getType() === Response::TYPE_FINAL; + if ($isLastForRequest) { + $this->pendingRequestsCount--; + } + + if ('' != $tag) { + if ($this->isRequestActive($tag, self::FILTER_CALLBACK)) { + if ($this->callbacks[$tag]($response, $this)) { + $this->cancelRequest($tag); + } elseif ($isLastForRequest) { + unset($this->callbacks[$tag]); + } + } else { + $this->responseBuffer[$tag][] = $response; + } + } + return $response; + } +} diff --git a/system/autoload/PEAR2/Net/RouterOS/Communicator.php b/system/autoload/PEAR2/Net/RouterOS/Communicator.php new file mode 100644 index 0000000..8176cfd --- /dev/null +++ b/system/autoload/PEAR2/Net/RouterOS/Communicator.php @@ -0,0 +1,671 @@ + + * @copyright 2011 Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 1.0.0b5 + * @link http://pear2.php.net/PEAR2_Net_RouterOS + */ +/** + * The namespace declaration. + */ +namespace PEAR2\Net\RouterOS; + +/** + * Using transmitters. + */ +use PEAR2\Net\Transmitter as T; + +/** + * A RouterOS communicator. + * + * Implementation of the RouterOS API protocol. Unlike the other classes in this + * package, this class doesn't provide any conviniences beyond the low level + * implementation details (automatic word length encoding/decoding, charset + * translation and data integrity), and because of that, its direct usage is + * strongly discouraged. + * + * @category Net + * @package PEAR2_Net_RouterOS + * @author Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Net_RouterOS + * @see Client + */ +class Communicator +{ + /** + * Used when getting/setting all (default) charsets. + */ + const CHARSET_ALL = -1; + + /** + * Used when getting/setting the (default) remote charset. + * + * The remote charset is the charset in which RouterOS stores its data. + * If you want to keep compatibility with your Winbox, this charset should + * match the default charset from your Windows' regional settings. + */ + const CHARSET_REMOTE = 0; + + /** + * Used when getting/setting the (default) local charset. + * + * The local charset is the charset in which the data from RouterOS will be + * returned as. This charset should match the charset of the place the data + * will eventually be written to. + */ + const CHARSET_LOCAL = 1; + + /** + * @var array An array with the default charset types as keys, and the + * default charsets as values. + */ + protected static $defaultCharsets = array( + self::CHARSET_REMOTE => null, + self::CHARSET_LOCAL => null + ); + + /** + * @var array An array with the current charset types as keys, and the + * current charsets as values. + */ + protected $charsets = array(); + + /** + * @var T\TcpClient The transmitter for the connection. + */ + protected $trans; + + /** + * Creates a new connection with the specified options. + * + * @param string $host Hostname (IP or domain) of the RouterOS server. + * @param int|null $port The port on which the RouterOS server provides + * the API service. You can also specify NULL, in which case the port + * will automatically be chosen between 8728 and 8729, depending on the + * value of $crypto. + * @param bool $persist Whether or not the connection should be a + * persistent one. + * @param float $timeout The timeout for the connection. + * @param string $key A string that uniquely identifies the + * connection. + * @param string $crypto The encryption for this connection. Must be one + * of the PEAR2\Net\Transmitter\NetworkStream::CRYPTO_* constants. Off + * by default. RouterOS currently supports only TLS, but the setting is + * provided in this fashion for forward compatibility's sake. And for + * the sake of simplicity, if you specify an encryption, don't specify a + * context and your default context uses the value "DEFAULT" for + * ciphers, "ADH" will be automatically added to the list of ciphers. + * @param resource $context A context for the socket. + * + * @see sendWord() + */ + public function __construct( + $host, + $port = 8728, + $persist = false, + $timeout = null, + $key = '', + $crypto = T\NetworkStream::CRYPTO_OFF, + $context = null + ) { + $isUnencrypted = T\NetworkStream::CRYPTO_OFF === $crypto; + if (($context === null) && !$isUnencrypted) { + $context = stream_context_get_default(); + $opts = stream_context_get_options($context); + if (!isset($opts['ssl']['ciphers']) + || 'DEFAULT' === $opts['ssl']['ciphers'] + ) { + stream_context_set_option($context, 'ssl', 'ciphers', 'ADH'); + } + } + // @codeCoverageIgnoreStart + // The $port is customizable in testing. + if (null === $port) { + $port = $isUnencrypted ? 8728 : 8729; + } + // @codeCoverageIgnoreEnd + + try { + $this->trans = new T\TcpClient( + $host, + $port, + $persist, + $timeout, + $key, + $crypto, + $context + ); + } catch (T\Exception $e) { + throw new SocketException( + 'Error connecting to RouterOS', + SocketException::CODE_CONNECTION_FAIL, + $e + ); + } + $this->setCharset( + self::getDefaultCharset(self::CHARSET_ALL), + self::CHARSET_ALL + ); + } + + /** + * A shorthand gateway. + * + * This is a magic PHP method that allows you to call the object as a + * function. Depending on the argument given, one of the other functions in + * the class is invoked and its returned value is returned by this function. + * + * @param string $string A string of the word to send, or NULL to get the + * next word as a string. + * + * @return int|string If a string is provided, returns the number of bytes + * sent, otherwise retuns the next word as a string. + */ + public function __invoke($string = null) + { + return null === $string ? $this->getNextWord() + : $this->sendWord($string); + } + + /** + * Checks whether a variable is a seekable stream resource. + * + * @param mixed $var The value to check. + * + * @return bool TRUE if $var is a seekable stream, FALSE otherwise. + */ + public static function isSeekableStream($var) + { + if (T\Stream::isStream($var)) { + $meta = stream_get_meta_data($var); + return $meta['seekable']; + } + return false; + } + + /** + * Uses iconv to convert a stream from one charset to another. + * + * @param string $inCharset The charset of the stream. + * @param string $outCharset The desired resulting charset. + * @param resource $stream The stream to convert. The stream is assumed + * to be seekable, and is read from its current position to its end, + * after which, it is seeked back to its starting position. + * + * @return resource A new stream that uses the $out_charset. The stream is a + * subset from the original stream, from its current position to its + * end, seeked at its start. + */ + public static function iconvStream($inCharset, $outCharset, $stream) + { + $bytes = 0; + $result = fopen('php://temp', 'r+b'); + $iconvFilter = stream_filter_append( + $result, + 'convert.iconv.' . $inCharset . '.' . $outCharset, + STREAM_FILTER_WRITE + ); + + flock($stream, LOCK_SH); + while (!feof($stream)) { + $bytes += stream_copy_to_stream($stream, $result, 0xFFFFF); + } + fseek($stream, -$bytes, SEEK_CUR); + flock($stream, LOCK_UN); + + stream_filter_remove($iconvFilter); + rewind($result); + return $result; + } + + /** + * Sets the default charset(s) for new connections. + * + * @param mixed $charset The charset to set. If $charsetType is + * {@link self::CHARSET_ALL}, you can supply either a string to use for + * all charsets, or an array with the charset types as keys, and the + * charsets as values. + * @param int $charsetType Which charset to set. Valid values are the + * CHARSET_* constants. Any other value is treated as + * {@link self::CHARSET_ALL}. + * + * @return string|array The old charset. If $charsetType is + * {@link self::CHARSET_ALL}, the old values will be returned as an + * array with the types as keys, and charsets as values. + * @see setCharset() + */ + public static function setDefaultCharset( + $charset, + $charsetType = self::CHARSET_ALL + ) { + if (array_key_exists($charsetType, self::$defaultCharsets)) { + $oldCharset = self::$defaultCharsets[$charsetType]; + self::$defaultCharsets[$charsetType] = $charset; + return $oldCharset; + } else { + $oldCharsets = self::$defaultCharsets; + self::$defaultCharsets = is_array($charset) ? $charset : array_fill( + 0, + count(self::$defaultCharsets), + $charset + ); + return $oldCharsets; + } + } + + /** + * Gets the default charset(s). + * + * @param int $charsetType Which charset to get. Valid values are the + * CHARSET_* constants. Any other value is treated as + * {@link self::CHARSET_ALL}. + * + * @return string|array The current charset. If $charsetType is + * {@link self::CHARSET_ALL}, the current values will be returned as an + * array with the types as keys, and charsets as values. + * @see setDefaultCharset() + */ + public static function getDefaultCharset($charsetType) + { + return array_key_exists($charsetType, self::$defaultCharsets) + ? self::$defaultCharsets[$charsetType] : self::$defaultCharsets; + } + + /** + * Gets the length of a seekable stream. + * + * Gets the length of a seekable stream. + * + * @param resource $stream The stream to check. The stream is assumed to be + * seekable. + * + * @return double The number of bytes in the stream between its current + * position and its end. + */ + public static function seekableStreamLength($stream) + { + $streamPosition = (double) sprintf('%u', ftell($stream)); + fseek($stream, 0, SEEK_END); + $streamLength = ((double) sprintf('%u', ftell($stream))) + - $streamPosition; + fseek($stream, $streamPosition, SEEK_SET); + return $streamLength; + } + + /** + * Sets the charset(s) for this connection. + * + * Sets the charset(s) for this connection. The specified charset(s) will be + * used for all future words. When sending, {@link self::CHARSET_LOCAL} is + * converted to {@link self::CHARSET_REMOTE}, and when receiving, + * {@link self::CHARSET_REMOTE} is converted to {@link self::CHARSET_LOCAL}. + * Setting NULL to either charset will disable charset convertion, and data + * will be both sent and received "as is". + * + * @param mixed $charset The charset to set. If $charsetType is + * {@link self::CHARSET_ALL}, you can supply either a string to use for + * all charsets, or an array with the charset types as keys, and the + * charsets as values. + * @param int $charsetType Which charset to set. Valid values are the + * CHARSET_* constants. Any other value is treated as + * {@link self::CHARSET_ALL}. + * + * @return string|array The old charset. If $charsetType is + * {@link self::CHARSET_ALL}, the old values will be returned as an + * array with the types as keys, and charsets as values. + * @see setDefaultCharset() + */ + public function setCharset($charset, $charsetType = self::CHARSET_ALL) + { + if (array_key_exists($charsetType, $this->charsets)) { + $oldCharset = $this->charsets[$charsetType]; + $this->charsets[$charsetType] = $charset; + return $oldCharset; + } else { + $oldCharsets = $this->charsets; + $this->charsets = is_array($charset) ? $charset : array_fill( + 0, + count($this->charsets), + $charset + ); + return $oldCharsets; + } + } + + /** + * Gets the charset(s) for this connection. + * + * @param int $charsetType Which charset to get. Valid values are the + * CHARSET_* constants. Any other value is treated as + * {@link self::CHARSET_ALL}. + * + * @return string|array The current charset. If $charsetType is + * {@link self::CHARSET_ALL}, the current values will be returned as an + * array with the types as keys, and charsets as values. + * @see getDefaultCharset() + * @see setCharset() + */ + public function getCharset($charsetType) + { + return array_key_exists($charsetType, $this->charsets) + ? $this->charsets[$charsetType] : $this->charsets; + } + + /** + * Gets the transmitter for this connection. + * + * @return T\TcpClient The transmitter for this connection. + */ + public function getTransmitter() + { + return $this->trans; + } + + /** + * Sends a word. + * + * Sends a word and automatically encodes its length when doing so. + * + * @param string $word The word to send. + * + * @return int The number of bytes sent. + * @see sendWordFromStream() + * @see getNextWord() + */ + public function sendWord($word) + { + if (null !== ($remoteCharset = $this->getCharset(self::CHARSET_REMOTE)) + && null !== ($localCharset = $this->getCharset(self::CHARSET_LOCAL)) + ) { + $word = iconv( + $localCharset, + $remoteCharset . '//IGNORE//TRANSLIT', + $word + ); + } + $length = strlen($word); + static::verifyLengthSupport($length); + if ($this->trans->isPersistent()) { + $old = $this->trans->lock(T\Stream::DIRECTION_SEND); + $bytes = $this->trans->send(self::encodeLength($length) . $word); + $this->trans->lock($old, true); + return $bytes; + } + return $this->trans->send(self::encodeLength($length) . $word); + } + + /** + * Sends a word based on a stream. + * + * Sends a word based on a stream and automatically encodes its length when + * doing so. The stream is read from its current position to its end, and + * then returned to its current position. Because of those operations, the + * supplied stream must be seekable. + * + * @param string $prefix A string to prepend before the stream contents. + * @param resource $stream The seekable stream to send. + * + * @return int The number of bytes sent. + * @see sendWord() + */ + public function sendWordFromStream($prefix, $stream) + { + if (!self::isSeekableStream($stream)) { + throw new InvalidArgumentException( + 'The stream must be seekable.', + InvalidArgumentException::CODE_SEEKABLE_REQUIRED + ); + } + if (null !== ($remoteCharset = $this->getCharset(self::CHARSET_REMOTE)) + && null !== ($localCharset = $this->getCharset(self::CHARSET_LOCAL)) + ) { + $prefix = iconv( + $localCharset, + $remoteCharset . '//IGNORE//TRANSLIT', + $prefix + ); + $stream = self::iconvStream( + $localCharset, + $remoteCharset . '//IGNORE//TRANSLIT', + $stream + ); + } + + flock($stream, LOCK_SH); + $totalLength = strlen($prefix) + self::seekableStreamLength($stream); + static::verifyLengthSupport($totalLength); + + $bytes = $this->trans->send(self::encodeLength($totalLength) . $prefix); + $bytes += $this->trans->send($stream); + + flock($stream, LOCK_UN); + return $bytes; + } + + /** + * Verifies that the length is supported. + * + * Verifies if the specified length is supported by the API. Throws a + * {@link LengthException} if that's not the case. Currently, RouterOS + * supports words up to 0xFFFFFFFF in length, so that's the only check + * performed. + * + * @param int $length The length to verify. + * + * @return void + */ + protected static function verifyLengthSupport($length) + { + if ($length > 0xFFFFFFFF) { + throw new LengthException( + 'Words with length above 0xFFFFFFFF are not supported.', + LengthException::CODE_UNSUPPORTED, + null, + $length + ); + } + } + + /** + * Encodes the length as requred by the RouterOS API. + * + * @param int $length The length to encode. + * + * @return string The encoded length. + */ + public static function encodeLength($length) + { + if ($length < 0) { + throw new LengthException( + 'Length must not be negative.', + LengthException::CODE_INVALID, + null, + $length + ); + } elseif ($length < 0x80) { + return chr($length); + } elseif ($length < 0x4000) { + return pack('n', $length |= 0x8000); + } elseif ($length < 0x200000) { + $length |= 0xC00000; + return pack('n', $length >> 8) . chr($length & 0xFF); + } elseif ($length < 0x10000000) { + return pack('N', $length |= 0xE0000000); + } elseif ($length <= 0xFFFFFFFF) { + return chr(0xF0) . pack('N', $length); + } elseif ($length <= 0x7FFFFFFFF) { + $length = 'f' . base_convert($length, 10, 16); + return chr(hexdec(substr($length, 0, 2))) . + pack('N', hexdec(substr($length, 2))); + } + throw new LengthException( + 'Length must not be above 0x7FFFFFFFF.', + LengthException::CODE_BEYOND_SHEME, + null, + $length + ); + } + + /** + * Get the next word in queue as a string. + * + * Get the next word in queue as a string, after automatically decoding its + * length. + * + * @return string The word. + * @see close() + */ + public function getNextWord() + { + if ($this->trans->isPersistent()) { + $old = $this->trans->lock(T\Stream::DIRECTION_RECEIVE); + $word = $this->trans->receive( + self::decodeLength($this->trans), + 'word' + ); + $this->trans->lock($old, true); + } else { + $word = $this->trans->receive( + self::decodeLength($this->trans), + 'word' + ); + } + + if (null !== ($remoteCharset = $this->getCharset(self::CHARSET_REMOTE)) + && null !== ($localCharset = $this->getCharset(self::CHARSET_LOCAL)) + ) { + $word = iconv( + $remoteCharset, + $localCharset . '//IGNORE//TRANSLIT', + $word + ); + } + + return $word; + } + + /** + * Get the next word in queue as a stream. + * + * Get the next word in queue as a stream, after automatically decoding its + * length. + * + * @return resource The word, as a stream. + * @see close() + */ + public function getNextWordAsStream() + { + $filters = new T\FilterCollection(); + if (null !== ($remoteCharset = $this->getCharset(self::CHARSET_REMOTE)) + && null !== ($localCharset = $this->getCharset(self::CHARSET_LOCAL)) + ) { + $filters->append( + 'convert.iconv.' . + $remoteCharset . '.' . $localCharset . '//IGNORE//TRANSLIT' + ); + } + + if ($this->trans->isPersistent()) { + $old = $this->trans->lock(T\Stream::DIRECTION_RECEIVE); + $stream = $this->trans->receiveStream( + self::decodeLength($this->trans), + $filters, + 'stream word' + ); + $this->trans->lock($old, true); + } else { + $stream = $this->trans->receiveStream( + self::decodeLength($this->trans), + $filters, + 'stream word' + ); + } + + return $stream; + } + + /** + * Decodes the lenght of the incoming message. + * + * Decodes the lenght of the incoming message, as specified by the RouterOS + * API. + * + * @param T\Stream $trans The transmitter from which to decode the length of + * the incoming message. + * + * @return int The decoded length. + */ + public static function decodeLength(T\Stream $trans) + { + if ($trans->isPersistent() && $trans instanceof T\TcpClient) { + $old = $trans->lock($trans::DIRECTION_RECEIVE); + $length = self::_decodeLength($trans); + $trans->lock($old, true); + return $length; + } + return self::_decodeLength($trans); + } + + /** + * Decodes the lenght of the incoming message. + * + * Decodes the lenght of the incoming message, as specified by the RouterOS + * API. + * + * Difference with the non private function is that this one doesn't perform + * locking if the connection is a persistent one. + * + * @param T\Stream $trans The transmitter from which to decode the length of + * the incoming message. + * + * @return int The decoded length. + */ + private static function _decodeLength(T\Stream $trans) + { + $byte = ord($trans->receive(1, 'initial length byte')); + if ($byte & 0x80) { + if (($byte & 0xC0) === 0x80) { + return (($byte & 077) << 8 ) + ord($trans->receive(1)); + } elseif (($byte & 0xE0) === 0xC0) { + $rem = unpack('n~', $trans->receive(2)); + return (($byte & 037) << 16 ) + $rem['~']; + } elseif (($byte & 0xF0) === 0xE0) { + $rem = unpack('n~/C~~', $trans->receive(3)); + return (($byte & 017) << 24 ) + ($rem['~'] << 8) + $rem['~~']; + } elseif (($byte & 0xF8) === 0xF0) { + $rem = unpack('N~', $trans->receive(4)); + return (($byte & 007) * 0x100000000/* '<< 32' or '2^32' */) + + (double) sprintf('%u', $rem['~']); + } + throw new NotSupportedException( + 'Unknown control byte encountered.', + NotSupportedException::CODE_CONTROL_BYTE, + null, + $byte + ); + } else { + return $byte; + } + } + + /** + * Closes the opened connection, even if it is a persistent one. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function close() + { + return $this->trans->close(); + } +} diff --git a/system/autoload/PEAR2/Net/RouterOS/DataFlowException.php b/system/autoload/PEAR2/Net/RouterOS/DataFlowException.php new file mode 100644 index 0000000..2d7f279 --- /dev/null +++ b/system/autoload/PEAR2/Net/RouterOS/DataFlowException.php @@ -0,0 +1,44 @@ + + * @copyright 2011 Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 1.0.0b5 + * @link http://pear2.php.net/PEAR2_Net_RouterOS + */ +/** + * The namespace declaration. + */ +namespace PEAR2\Net\RouterOS; + +/** + * Base of this class. + */ +use RuntimeException; + +/** + * Exception thrown when the request/response cycle goes an unexpected way. + * + * @category Net + * @package PEAR2_Net_RouterOS + * @author Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Net_RouterOS + */ +class DataFlowException extends RuntimeException implements Exception +{ + const CODE_INVALID_CREDENTIALS = 10000; + const CODE_TAG_REQUIRED = 10500; + const CODE_TAG_UNIQUE = 10501; + const CODE_UNKNOWN_REQUEST = 10900; + const CODE_CANCEL_FAIL = 11200; +} diff --git a/system/autoload/PEAR2/Net/RouterOS/Exception.php b/system/autoload/PEAR2/Net/RouterOS/Exception.php new file mode 100644 index 0000000..842f128 --- /dev/null +++ b/system/autoload/PEAR2/Net/RouterOS/Exception.php @@ -0,0 +1,34 @@ + + * @copyright 2011 Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 1.0.0b5 + * @link http://pear2.php.net/PEAR2_Net_RouterOS + */ +/** + * The namespace declaration. + */ +namespace PEAR2\Net\RouterOS; + +/** + * Generic exception class of this package. + * + * @category Net + * @package PEAR2_Net_RouterOS + * @author Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Net_RouterOS + */ +interface Exception +{ +} diff --git a/system/autoload/PEAR2/Net/RouterOS/InvalidArgumentException.php b/system/autoload/PEAR2/Net/RouterOS/InvalidArgumentException.php new file mode 100644 index 0000000..e0181da --- /dev/null +++ b/system/autoload/PEAR2/Net/RouterOS/InvalidArgumentException.php @@ -0,0 +1,43 @@ + + * @copyright 2011 Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 1.0.0b5 + * @link http://pear2.php.net/PEAR2_Net_RouterOS + */ +/** + * The namespace declaration. + */ +namespace PEAR2\Net\RouterOS; + +use InvalidArgumentException as I; + +/** + * Exception thrown when there's something wrong with message arguments. + * + * @category Net + * @package PEAR2_Net_RouterOS + * @author Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Net_RouterOS + */ +class InvalidArgumentException extends I implements Exception +{ + const CODE_SEEKABLE_REQUIRED = 1100; + const CODE_NAME_INVALID = 20100; + const CODE_ABSOLUTE_REQUIRED = 40200; + const CODE_CMD_UNRESOLVABLE = 40201; + const CODE_CMD_INVALID = 40202; + const CODE_NAME_UNPARSABLE = 41000; + const CODE_VALUE_UNPARSABLE = 41001; +} diff --git a/system/autoload/PEAR2/Net/RouterOS/LengthException.php b/system/autoload/PEAR2/Net/RouterOS/LengthException.php new file mode 100644 index 0000000..cd69adb --- /dev/null +++ b/system/autoload/PEAR2/Net/RouterOS/LengthException.php @@ -0,0 +1,93 @@ + + * @copyright 2011 Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 1.0.0b5 + * @link http://pear2.php.net/PEAR2_Net_RouterOS + */ +/** + * The namespace declaration. + */ +namespace PEAR2\Net\RouterOS; + +/** + * Base of this class. + */ +use LengthException as L; + +/** + * Exception thrown when there is a problem with a word's length. + * + * @category Net + * @package PEAR2_Net_RouterOS + * @author Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Net_RouterOS + */ +class LengthException extends L implements Exception +{ + + const CODE_UNSUPPORTED = 1200; + const CODE_INVALID = 1300; + const CODE_BEYOND_SHEME = 1301; + + /** + * + * @var mixed The problematic length. + */ + private $_length; + + /** + * Creates a new LengthException. + * + * @param string $message The Exception message to throw. + * @param int $code The Exception code. + * @param \Exception $previous The previous exception used for the exception + * chaining. + * @param number $length The length. + */ + public function __construct( + $message, + $code = 0, + $previous = null, + $length = null + ) { + parent::__construct($message, $code, $previous); + $this->_length = $length; + } + + /** + * Gets the length. + * + * @return number The length. + */ + public function getLength() + { + return $this->_length; + } + + // @codeCoverageIgnoreStart + // String representation is not reliable in testing + + /** + * Returns a string representation of the exception. + * + * @return string The exception as a string. + */ + public function __toString() + { + return parent::__toString() . "\nLength:{$this->_length}"; + } + + // @codeCoverageIgnoreEnd +} diff --git a/system/autoload/PEAR2/Net/RouterOS/Message.php b/system/autoload/PEAR2/Net/RouterOS/Message.php new file mode 100644 index 0000000..8935b31 --- /dev/null +++ b/system/autoload/PEAR2/Net/RouterOS/Message.php @@ -0,0 +1,237 @@ + + * @copyright 2011 Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 1.0.0b5 + * @link http://pear2.php.net/PEAR2_Net_RouterOS + */ +/** + * The namespace declaration. + */ +namespace PEAR2\Net\RouterOS; + +/** + * Implements this interface. + */ +use Countable; + +/** + * Implements this interface. + */ +use IteratorAggregate; + +/** + * Requred for IteratorAggregate::getIterator() to work properly with foreach. + */ +use ArrayObject; + +/** + * Represents a RouterOS message. + * + * @category Net + * @package PEAR2_Net_RouterOS + * @author Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Net_RouterOS + */ +abstract class Message implements IteratorAggregate, Countable +{ + + /** + * @var array An array with message attributes. Each array key is the the + * name of an attribute, and the correspding array value is the value + * for that attribute. + */ + protected $attributes = array(); + + /** + * @var string An optional tag to associate the message with. + */ + private $_tag = null; + + /** + * A shorthand gateway. + * + * This is a magic PHP method that allows you to call the object as a + * function. Depending on the argument given, one of the other functions in + * the class is invoked and its returned value is returned by this function. + * + * @param string $name The name of an attribute to get the value of, or NULL + * to get the tag. + * + * @return string|resource The value of the specified attribute, + * or the tag if NULL is provided. + */ + public function __invoke($name = null) + { + if (null === $name) { + return $this->getTag(); + } + return $this->getAttribute($name); + } + + /** + * Sanitizes a name of an attribute (message or query one). + * + * @param mixed $name The name to sanitize. + * + * @return string The sanitized name. + */ + public static function sanitizeAttributeName($name) + { + $name = (string) $name; + if ((empty($name) && $name !== '0') + || preg_match('/[=\s]/s', $name) + ) { + throw new InvalidArgumentException( + 'Invalid name of argument supplied.', + InvalidArgumentException::CODE_NAME_INVALID + ); + } + return $name; + } + + /** + * Sanitizes a value of an attribute (message or query one). + * + * @param mixed $value The value to sanitize. + * + * @return string The sanitized value. + */ + public static function sanitizeAttributeValue($value) + { + if (Communicator::isSeekableStream($value)) { + return $value; + } else { + return (string) $value; + } + } + + /** + * Gets the tag that the message is associated with. + * + * @return string The current tag or NULL if there isn't a tag. + * @see setTag() + */ + public function getTag() + { + return $this->_tag; + } + + /** + * Sets the tag to associate the request with. + * + * Sets the tag to associate the message with. Setting NULL erases the + * currently set tag. + * + * @param string $tag The tag to set. + * + * @return $this The message object. + * @see getTag() + */ + protected function setTag($tag) + { + $this->_tag = (null === $tag) ? null : (string) $tag; + return $this; + } + + /** + * Gets the value of an attribute. + * + * @param string $name The name of the attribute. + * + * @return string|resource|null The value of the specified attribute. + * Returns NULL if such an attribute is not set. + * @see setAttribute() + */ + protected function getAttribute($name) + { + $name = self::sanitizeAttributeName($name); + if (array_key_exists($name, $this->attributes)) { + return $this->attributes[$name]; + } + return null; + } + + /** + * Gets all arguments in an array. + * + * @return ArrayObject An ArrayObject with the keys being argument names, + * and the array values being argument values. + * @see getArgument() + * @see setArgument() + */ + public function getIterator() + { + return new ArrayObject($this->attributes); + } + + /** + * Counts the number of arguments. + * + * @param int $mode The counter mode. + * Either COUNT_NORMAL or COUNT_RECURSIVE. + * When in normal mode, counts the number of arguments. + * When in recursive mode, counts the number of API words + * (including the empty word at the end). + * + * @return int The number of arguments/words. + */ + public function count($mode = COUNT_NORMAL) + { + $result = count($this->attributes); + if ($mode !== COUNT_NORMAL) { + $result += 2/*first+last word*/ + + (int)(null !== $this->getTag()); + } + return $result; + } + + /** + * Sets an attribute for the message. + * + * @param string $name Name of the attribute. + * @param string|resource|null $value Value of the attribute as a string or + * seekable stream. + * Setting the value to NULL removes an argument of this name. + * If a seekable stream is provided, it is sent from its current + * posistion to its end, and the pointer is seeked back to its current + * position after sending. + * Non seekable streams, as well as all other types, are casted to a + * string. + * + * @return $this The message object. + * @see getArgument() + */ + protected function setAttribute($name, $value = '') + { + if (null === $value) { + unset($this->attributes[self::sanitizeAttributeName($name)]); + } else { + $this->attributes[self::sanitizeAttributeName($name)] + = self::sanitizeAttributeValue($value); + } + return $this; + } + + /** + * Removes all attributes from the message. + * + * @return $this The message object. + */ + protected function removeAllAttributes() + { + $this->attributes = array(); + return $this; + } +} diff --git a/system/autoload/PEAR2/Net/RouterOS/NotSupportedException.php b/system/autoload/PEAR2/Net/RouterOS/NotSupportedException.php new file mode 100644 index 0000000..2a7e309 --- /dev/null +++ b/system/autoload/PEAR2/Net/RouterOS/NotSupportedException.php @@ -0,0 +1,91 @@ + + * @copyright 2011 Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 1.0.0b5 + * @link http://pear2.php.net/PEAR2_Net_RouterOS + */ +/** + * The namespace declaration. + */ +namespace PEAR2\Net\RouterOS; + +/** + * Base of this class. + */ +use Exception as E; + +/** + * Exception thrown when encountering something not supported by RouterOS or + * this package. + * + * @category Net + * @package PEAR2_Net_RouterOS + * @author Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Net_RouterOS + */ +class NotSupportedException extends E implements Exception +{ + + const CODE_CONTROL_BYTE = 1601; + + /** + * @var mixed The unsuppported value. + */ + private $_value; + + /** + * Creates a new NotSupportedException. + * + * @param string $message The Exception message to throw. + * @param int $code The Exception code. + * @param \Exception $previous The previous exception used for the exception + * chaining. + * @param mixed $value The unsupported value. + */ + public function __construct( + $message, + $code = 0, + $previous = null, + $value = null + ) { + parent::__construct($message, $code, $previous); + $this->_value = $value; + } + + /** + * Gets the unsupported value. + * + * @return mixed The unsupported value. + */ + public function getValue() + { + return $this->_value; + } + + // @codeCoverageIgnoreStart + // String representation is not reliable in testing + + /** + * Returns a string representation of the exception. + * + * @return string The exception as a string. + */ + public function __toString() + { + return parent::__toString() . "\nValue:{$this->_value}"; + } + + // @codeCoverageIgnoreEnd +} diff --git a/system/autoload/PEAR2/Net/RouterOS/Query.php b/system/autoload/PEAR2/Net/RouterOS/Query.php new file mode 100644 index 0000000..4e759aa --- /dev/null +++ b/system/autoload/PEAR2/Net/RouterOS/Query.php @@ -0,0 +1,267 @@ + + * @copyright 2011 Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 1.0.0b5 + * @link http://pear2.php.net/PEAR2_Net_RouterOS + */ +/** + * The namespace declaration. + */ +namespace PEAR2\Net\RouterOS; + +/** + * Refers to transmitter direction constants. + */ +use PEAR2\Net\Transmitter as T; + +/** + * Represents a query for RouterOS requests. + * + * @category Net + * @package PEAR2_Net_RouterOS + * @author Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Net_RouterOS + */ +class Query +{ + + /** + * Checks if the property exists. + */ + const OP_EX = ''; + + /** + * Checks if the property does not exist. + */ + const OP_NEX = '-'; + + /** + * Checks if the property equals a certain value. + */ + const OP_EQ = '='; + + /** + * Checks if the property is less than a certain value. + */ + const OP_LT = '<'; + + /** + * Checks if the property is greather than a certain value. + */ + const OP_GT = '>'; + + /** + * @var array An array of the words forming the query. Each value is an + * array with the first member being the predicate (operator and name), + * and the second member being the value for the predicate. + */ + protected $words = array(); + + /** + * This class is not to be instantiated normally, but by static methods + * instead. Use {@link static::where()} to create an instance of it. + */ + private function __construct() + { + + } + + /** + * Sanitizes the operator of a condition. + * + * @param string $operator The operator to sanitize. + * + * @return string The sanitized operator. + */ + protected static function sanitizeOperator($operator) + { + $operator = (string) $operator; + switch ($operator) { + case Query::OP_EX: + case Query::OP_NEX: + case Query::OP_EQ: + case Query::OP_LT: + case Query::OP_GT: + return $operator; + default: + throw new UnexpectedValueException( + 'Unknown operator specified', + UnexpectedValueException::CODE_ACTION_UNKNOWN, + null, + $operator + ); + } + } + + /** + * Creates a new query with an initial condition. + * + * @param string $name The name of the property to test. + * @param string|resource|null $value Value of the property as a string + * or seekable stream. Not required for existence tests. + * If a seekable stream is provided, it is sent from its current + * posistion to its end, and the pointer is seeked back to its current + * position after sending. + * Non seekable streams, as well as all other types, are casted to a + * string. + * @param string $operator One of the OP_* constants. + * Describes the operation to perform. + * + * @return static A new query object. + */ + public static function where( + $name, + $value = null, + $operator = self::OP_EX + ) { + $query = new static; + return $query->addWhere($name, $value, $operator); + } + + /** + * Negates the query. + * + * @return $this The query object. + */ + public function not() + { + $this->words[] = array('#!', null); + return $this; + } + + /** + * Adds a condition as an alternative to the query. + * + * @param string $name The name of the property to test. + * @param string|resource|null $value Value of the property as a string + * or seekable stream. Not required for existence tests. + * If a seekable stream is provided, it is sent from its current + * posistion to its end, and the pointer is seeked back to its current + * position after sending. + * Non seekable streams, as well as all other types, are casted to a + * string. + * @param string $operator One of the OP_* constants. + * Describes the operation to perform. + * + * @return $this The query object. + */ + public function orWhere($name, $value = null, $operator = self::OP_EX) + { + $this->addWhere($name, $value, $operator)->words[] = array('#|', null); + return $this; + } + + /** + * Adds a condition in addition to the query. + * + * @param string $name The name of the property to test. + * @param string|resource|null $value Value of the property as a string + * or seekable stream. Not required for existence tests. + * If a seekable stream is provided, it is sent from its current + * posistion to its end, and the pointer is seeked back to its current + * position after sending. + * Non seekable streams, as well as all other types, are casted to a + * string. + * @param string $operator One of the OP_* constants. + * Describes the operation to perform. + * + * @return $this The query object. + */ + public function andWhere($name, $value = null, $operator = self::OP_EX) + { + $this->addWhere($name, $value, $operator)->words[] = array('#&', null); + return $this; + } + + /** + * Sends the query over a communicator. + * + * @param Communicator $com The communicator to send the query over. + * + * @return int The number of bytes sent. + */ + public function send(Communicator $com) + { + if ($com->getTransmitter()->isPersistent()) { + $old = $com->getTransmitter()->lock(T\Stream::DIRECTION_SEND); + $bytes = $this->_send($com); + $com->getTransmitter()->lock($old, true); + return $bytes; + } + return $this->_send($com); + } + + /** + * Sends the query over a communicator. + * + * The only difference with the non private equivalent is that this one does + * not do locking. + * + * @param Communicator $com The communicator to send the query over. + * + * @return int The number of bytes sent. + */ + private function _send(Communicator $com) + { + if (!$com->getTransmitter()->isAcceptingData()) { + throw new SocketException( + 'Transmitter is invalid. Sending aborted.', + SocketException::CODE_QUERY_SEND_FAIL + ); + } + $bytes = 0; + foreach ($this->words as $queryWord) { + list($predicate, $value) = $queryWord; + $prefix = '?' . $predicate; + if (null === $value) { + $bytes += $com->sendWord($prefix); + } else { + $prefix .= '='; + if (is_string($value)) { + $bytes += $com->sendWord($prefix . $value); + } else { + $bytes += $com->sendWordFromStream($prefix, $value); + } + } + } + return $bytes; + } + + /** + * Adds a condition. + * + * @param string $name The name of the property to test. + * @param string|resource|null $value Value of the property as a string + * or seekable stream. Not required for existence tests. + * If a seekable stream is provided, it is sent from its current + * posistion to its end, and the pointer is seeked back to its current + * position after sending. + * Non seekable streams, as well as all other types, are casted to a + * string. + * @param string $operator One of the ACTION_* constants. + * Describes the operation to perform. + * + * @return $this The query object. + */ + protected function addWhere($name, $value, $operator) + { + $this->words[] = array( + static::sanitizeOperator($operator) + . Message::sanitizeAttributeName($name), + (null === $value ? null : Message::sanitizeAttributeValue($value)) + ); + return $this; + } +} diff --git a/system/autoload/PEAR2/Net/RouterOS/Registry.php b/system/autoload/PEAR2/Net/RouterOS/Registry.php new file mode 100644 index 0000000..30d2e75 --- /dev/null +++ b/system/autoload/PEAR2/Net/RouterOS/Registry.php @@ -0,0 +1,279 @@ + + * @copyright 2011 Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 1.0.0b5 + * @link http://pear2.php.net/PEAR2_Net_RouterOS + */ +/** + * The namespace declaration. + */ +namespace PEAR2\Net\RouterOS; + +/** + * Uses shared memory to keep responses in. + */ +use PEAR2\Cache\SHM; + +/** + * A RouterOS registry. + * + * Provides functionality for managing the request/response flow. Particularly + * useful in persistent connections. + * + * Note that this class is not meant to be called directly. + * + * @category Net + * @package PEAR2_Net_RouterOS + * @author Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Net_RouterOS + */ +class Registry +{ + /** + * @var SHM The storage. + */ + protected $shm; + + /** + * @var int ID of request. Populated at first instance in request. + */ + protected static $requestId = -1; + + /** + * @var int ID to be given to next instance, after incrementing it. + */ + protected static $instanceIdSeed = -1; + + /** + * @var int ID of instance within the request. + */ + protected $instanceId; + + /** + * Creates a registry. + * + * @param string $uri An URI to bind the registry to. + */ + public function __construct($uri) + { + $this->shm = SHM::factory(__CLASS__ . ' ' . $uri); + if (-1 === self::$requestId) { + self::$requestId = $this->shm->add('requestId', 0) + ? 0 : $this->shm->inc('requestId'); + } + $this->instanceId = ++self::$instanceIdSeed; + $this->shm->add('responseBuffer_' . $this->getOwnershipTag(), array()); + } + + /** + * Parses a tag. + * + * Parses a tag to reveal the ownership part of it, and the original tag. + * + * @param string $tag The tag (as received) to parse. + * + * @return array An array with the first member being the ownership tag, and + * the second one being the original tag. + */ + public static function parseTag($tag) + { + if (null === $tag) { + return array(null, null); + } + $result = explode('__', $tag, 2); + $result[0] .= '__'; + if ('' === $result[1]) { + $result[1] = null; + } + return $result; + } + + /** + * Checks if this instance is the tagless mode owner. + * + * @return bool TRUE if this instance is the tagless mode owner, FALSE + * otherwise. + */ + public function isTaglessModeOwner() + { + $this->shm->lock('taglessModeOwner'); + $result = $this->shm->exists('taglessModeOwner') + && $this->getOwnershipTag() === $this->shm->get('taglessModeOwner'); + $this->shm->unlock('taglessModeOwner'); + return $result; + } + + /** + * Sets the "tagless mode" setting. + * + * While in tagless mode, this instance will claim owhership of any + * responses without a tag. While not in this mode, any requests without a + * tag will be given to all instances. + * + * Regardless of mode, if the type of the response is + * {@link Response::TYPE_FATAL}, it will be given to all instances. + * + * @param bool $taglessMode TRUE to claim tagless ownership, FALSE to + * release such ownership, if taken. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function setTaglessMode($taglessMode) + { + return $taglessMode + ? ($this->shm->lock('taglessMode') + && $this->shm->lock('taglessModeOwner') + && $this->shm->add('taglessModeOwner', $this->getOwnershipTag()) + && $this->shm->unlock('taglessModeOwner')) + : ($this->isTaglessModeOwner() + && $this->shm->lock('taglessModeOwner') + && $this->shm->delete('taglessModeOwner') + && $this->shm->unlock('taglessModeOwner') + && $this->shm->unlock('taglessMode')); + } + + /** + * Get the ownership tag for this instance. + * + * @return string The ownership tag for this registry instance. + */ + public function getOwnershipTag() + { + return self::$requestId . '_' . $this->instanceId . '__'; + } + + /** + * Add a response to the registry. + * + * @param Response $response The response to add. The caller of this + * function is responsible for ensuring that the ownership tag and the + * original tag are separated, so that only the original one remains in + * the response. + * @param string $ownershipTag The ownership tag that the response had. + * + * @return bool TRUE if the request was added to its buffer, FALSE if + * this instance owns the response, and therefore doesn't need to add + * the response to its buffer. + */ + public function add(Response $response, $ownershipTag) + { + if ($this->getOwnershipTag() === $ownershipTag + || ($this->isTaglessModeOwner() + && $response->getType() !== Response::TYPE_FATAL) + ) { + return false; + } + + if (null === $ownershipTag) { + $this->shm->lock('taglessModeOwner'); + if ($this->shm->exists('taglessModeOwner') + && $response->getType() !== Response::TYPE_FATAL + ) { + $ownershipTag = $this->shm->get('taglessModeOwner'); + $this->shm->unlock('taglessModeOwner'); + } else { + $this->shm->unlock('taglessModeOwner'); + foreach ($this->shm->getIterator( + '/^(responseBuffer\_)/', + true + ) as $targetBufferName) { + $this->_add($response, $targetBufferName); + } + return true; + } + } + + $this->_add($response, 'responseBuffer_' . $ownershipTag); + return true; + } + + /** + * Adds a response to a buffer. + * + * @param Response $response The response to add. + * @param string $targetBufferName The name of the buffer to add the + * response to. + * + * @return void + */ + private function _add(Response $response, $targetBufferName) + { + if ($this->shm->lock($targetBufferName)) { + $targetBuffer = $this->shm->get($targetBufferName); + $targetBuffer[] = $response; + $this->shm->set($targetBufferName, $targetBuffer); + $this->shm->unlock($targetBufferName); + } + } + + /** + * Gets the next response from this instance's buffer. + * + * @return Response|null The next response, or NULL if there isn't one. + */ + public function getNextResponse() + { + $response = null; + $targetBufferName = 'responseBuffer_' . $this->getOwnershipTag(); + if ($this->shm->exists($targetBufferName) + && $this->shm->lock($targetBufferName) + ) { + $targetBuffer = $this->shm->get($targetBufferName); + if (!empty($targetBuffer)) { + $response = array_shift($targetBuffer); + $this->shm->set($targetBufferName, $targetBuffer); + } + $this->shm->unlock($targetBufferName); + } + return $response; + } + + /** + * Closes the registry. + * + * Closes the registry, meaning that all buffers are cleared. + * + * @return void + */ + public function close() + { + self::$requestId = -1; + self::$instanceIdSeed = -1; + $this->shm->clear(); + } + + /** + * Removes a buffer. + * + * @param string $targetBufferName The buffer to remove. + * + * @return void + */ + private function _close($targetBufferName) + { + if ($this->shm->lock($targetBufferName)) { + $this->shm->delete($targetBufferName); + $this->shm->unlock($targetBufferName); + } + } + + /** + * Removes this instance's buffer. + */ + public function __destruct() + { + $this->_close('responseBuffer_' . $this->getOwnershipTag()); + } +} diff --git a/system/autoload/PEAR2/Net/RouterOS/Request.php b/system/autoload/PEAR2/Net/RouterOS/Request.php new file mode 100644 index 0000000..eeba12f --- /dev/null +++ b/system/autoload/PEAR2/Net/RouterOS/Request.php @@ -0,0 +1,403 @@ + + * @copyright 2011 Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 1.0.0b5 + * @link http://pear2.php.net/PEAR2_Net_RouterOS + */ +/** + * The namespace declaration. + */ +namespace PEAR2\Net\RouterOS; + +/** + * Refers to transmitter direction constants. + */ +use PEAR2\Net\Transmitter as T; + +/** + * Represents a RouterOS request. + * + * @category Net + * @package PEAR2_Net_RouterOS + * @author Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Net_RouterOS + */ +class Request extends Message +{ + + /** + * @var string The command to be executed. + */ + private $_command; + + /** + * @var Query A query for the command. + */ + private $_query; + + /** + * Creates a request to send to RouterOS. + * + * @param string $command The command to send. Can also contain arguments + * expressed in a shell-like syntax. + * @param Query $query A query to associate with the request. + * @param string $tag The tag for the request. + * + * @see setCommand() + * @see setArgument() + * @see setTag() + * @see setQuery() + */ + public function __construct($command, Query $query = null, $tag = null) + { + if (false !== strpos($command, '=') + && false !== ($spaceBeforeEquals = strrpos( + strstr($command, '=', true), + ' ' + )) + ) { + $this->parseArgumentString(substr($command, $spaceBeforeEquals)); + $command = rtrim(substr($command, 0, $spaceBeforeEquals)); + } + $this->setCommand($command); + $this->setQuery($query); + $this->setTag($tag); + } + + /** + * A shorthand gateway. + * + * This is a magic PHP method that allows you to call the object as a + * function. Depending on the argument given, one of the other functions in + * the class is invoked and its returned value is returned by this function. + * + * @param Query|Communicator|string|null $arg A {@link Query} to associate + * the request with, a {@link Communicator} to send the request over, + * an argument to get the value of, or NULL to get the tag. If a + * second argument is provided, this becomes the name of the argument to + * set the value of, and the second argument is the value to set. + * + * @return string|resource|int|$this Whatever the long form + * function returns. + */ + public function __invoke($arg = null) + { + if (func_num_args() > 1) { + return $this->setArgument(func_get_arg(0), func_get_arg(1)); + } + if ($arg instanceof Query) { + return $this->setQuery($arg); + } + if ($arg instanceof Communicator) { + return $this->send($arg); + } + return parent::__invoke($arg); + } + + /** + * Sets the command to send to RouterOS. + * + * Sets the command to send to RouterOS. The command can use the API or CLI + * syntax of RouterOS, but either way, it must be absolute (begin with a + * "/") and without arguments. + * + * @param string $command The command to send. + * + * @return $this The request object. + * @see getCommand() + * @see setArgument() + */ + public function setCommand($command) + { + $command = (string) $command; + if (strpos($command, '/') !== 0) { + throw new InvalidArgumentException( + 'Commands must be absolute.', + InvalidArgumentException::CODE_ABSOLUTE_REQUIRED + ); + } + if (substr_count($command, '/') === 1) { + //Command line syntax convertion + $cmdParts = preg_split('#[\s/]+#sm', $command); + $cmdRes = array($cmdParts[0]); + for ($i = 1, $n = count($cmdParts); $i < $n; $i++) { + if ('..' === $cmdParts[$i]) { + $delIndex = count($cmdRes) - 1; + if ($delIndex < 1) { + throw new InvalidArgumentException( + 'Unable to resolve command', + InvalidArgumentException::CODE_CMD_UNRESOLVABLE + ); + } + unset($cmdRes[$delIndex]); + $cmdRes = array_values($cmdRes); + } else { + $cmdRes[] = $cmdParts[$i]; + } + } + $command = implode('/', $cmdRes); + } + if (!preg_match('#^/\S+$#sm', $command)) { + throw new InvalidArgumentException( + 'Invalid command supplied.', + InvalidArgumentException::CODE_CMD_INVALID + ); + } + $this->_command = $command; + return $this; + } + + /** + * Gets the command that will be send to RouterOS. + * + * Gets the command that will be send to RouterOS in its API syntax. + * + * @return string The command to send. + * @see setCommand() + */ + public function getCommand() + { + return $this->_command; + } + + /** + * Sets the query to send with the command. + * + * @param Query $query The query to be set. Setting NULL will remove the + * currently associated query. + * + * @return $this The request object. + * @see getQuery() + */ + public function setQuery(Query $query = null) + { + $this->_query = $query; + return $this; + } + + /** + * Gets the currently associated query + * + * @return Query The currently associated query. + * @see setQuery() + */ + public function getQuery() + { + return $this->_query; + } + + /** + * Sets the tag to associate the request with. + * + * Sets the tag to associate the request with. Setting NULL erases the + * currently set tag. + * + * @param string $tag The tag to set. + * + * @return $this The request object. + * @see getTag() + */ + public function setTag($tag) + { + return parent::setTag($tag); + } + + /** + * Sets an argument for the request. + * + * @param string $name Name of the argument. + * @param string|resource|null $value Value of the argument as a string or + * seekable stream. + * Setting the value to NULL removes an argument of this name. + * If a seekable stream is provided, it is sent from its current + * posistion to its end, and the pointer is seeked back to its current + * position after sending. + * Non seekable streams, as well as all other types, are casted to a + * string. + * + * @return $this The request object. + * @see getArgument() + */ + public function setArgument($name, $value = '') + { + return parent::setAttribute($name, $value); + } + + /** + * Gets the value of an argument. + * + * @param string $name The name of the argument. + * + * @return string|resource|null The value of the specified argument. + * Returns NULL if such an argument is not set. + * @see setAttribute() + */ + public function getArgument($name) + { + return parent::getAttribute($name); + } + + /** + * Removes all arguments from the request. + * + * @return $this The request object. + */ + public function removeAllArguments() + { + return parent::removeAllAttributes(); + } + + /** + * Sends a request over a communicator. + * + * @param Communicator $com The communicator to send the request over. + * @param Registry $reg An optional registry to sync the request with. + * + * @return int The number of bytes sent. + * @see Client::sendSync() + * @see Client::sendAsync() + */ + public function send(Communicator $com, Registry $reg = null) + { + if (null !== $reg + && (null != $this->getTag() || !$reg->isTaglessModeOwner()) + ) { + $originalTag = $this->getTag(); + $this->setTag($reg->getOwnershipTag() . $originalTag); + $bytes = $this->send($com); + $this->setTag($originalTag); + return $bytes; + } + if ($com->getTransmitter()->isPersistent()) { + $old = $com->getTransmitter()->lock(T\Stream::DIRECTION_SEND); + $bytes = $this->_send($com); + $com->getTransmitter()->lock($old, true); + return $bytes; + } + return $this->_send($com); + } + + /** + * Sends a request over a communicator. + * + * The only difference with the non private equivalent is that this one does + * not do locking. + * + * @param Communicator $com The communicator to send the request over. + * + * @return int The number of bytes sent. + * @see Client::sendSync() + * @see Client::sendAsync() + */ + private function _send(Communicator $com) + { + if (!$com->getTransmitter()->isAcceptingData()) { + throw new SocketException( + 'Transmitter is invalid. Sending aborted.', + SocketException::CODE_REQUEST_SEND_FAIL + ); + } + $bytes = 0; + $bytes += $com->sendWord($this->getCommand()); + if (null !== ($tag = $this->getTag())) { + $bytes += $com->sendWord('.tag=' . $tag); + } + foreach ($this->attributes as $name => $value) { + $prefix = '=' . $name . '='; + if (is_string($value)) { + $bytes += $com->sendWord($prefix . $value); + } else { + $bytes += $com->sendWordFromStream($prefix, $value); + } + } + $query = $this->getQuery(); + if ($query instanceof Query) { + $bytes += $query->send($com); + } + $bytes += $com->sendWord(''); + return $bytes; + } + + /** + * Parses the arguments of a command. + * + * @param string $string The argument string to parse. + * + * @return void + */ + protected function parseArgumentString($string) + { + /* + * Grammar: + * + * := (<<\s+>>, )*, + * := , ? + * := <<[^\=\s]+>> + * := "=", ( | ) + * := <<">>, <<([^"]|\\"|\\\\)*>>, <<">> + * := <<\S+>> + */ + + $token = ''; + $name = null; + while ($string = substr($string, strlen($token))) { + if (null === $name) { + if (preg_match('/^\s+([^\s=]+)/sS', $string, $matches)) { + $token = $matches[0]; + $name = $matches[1]; + } else { + throw new InvalidArgumentException( + "Parsing of argument name failed near '{$string}'", + InvalidArgumentException::CODE_NAME_UNPARSABLE + ); + } + } elseif (preg_match('/^\s/s', $string, $matches)) { + //Empty argument + $token = ''; + $this->setArgument($name); + $name = null; + } elseif (preg_match( + '/^="(([^\\\"]|\\\"|\\\\)*)"/sS', + $string, + $matches + )) { + $token = $matches[0]; + $this->setArgument( + $name, + str_replace( + array('\\"', '\\\\'), + array('"', '\\'), + $matches[1] + ) + ); + $name = null; + } elseif (preg_match('/^=(\S+)/sS', $string, $matches)) { + $token = $matches[0]; + $this->setArgument($name, $matches[1]); + $name = null; + } else { + throw new InvalidArgumentException( + "Parsing of argument value failed near '{$string}'", + InvalidArgumentException::CODE_VALUE_UNPARSABLE + ); + } + } + + if (null !== $name && ('' !== ($name = trim($name)))) { + $this->setArgument($name, ''); + } + + } +} diff --git a/system/autoload/PEAR2/Net/RouterOS/Response.php b/system/autoload/PEAR2/Net/RouterOS/Response.php new file mode 100644 index 0000000..c5fd414 --- /dev/null +++ b/system/autoload/PEAR2/Net/RouterOS/Response.php @@ -0,0 +1,335 @@ + + * @copyright 2011 Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 1.0.0b5 + * @link http://pear2.php.net/PEAR2_Net_RouterOS + */ +/** + * The namespace declaration. + */ +namespace PEAR2\Net\RouterOS; + +/** + * Refers to transmitter direction constants. + */ +use PEAR2\Net\Transmitter as T; + +/** + * Locks are released upon any exception from anywhere. + */ +use Exception as E; + +/** + * Represents a RouterOS response. + * + * @category Net + * @package PEAR2_Net_RouterOS + * @author Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Net_RouterOS + */ +class Response extends Message +{ + + /** + * The last response for a request. + */ + const TYPE_FINAL = '!done'; + + /** + * A response with data. + */ + const TYPE_DATA = '!re'; + + /** + * A response signifying error. + */ + const TYPE_ERROR = '!trap'; + + /** + * A response signifying a fatal error, due to which the connection would be + * terminated. + */ + const TYPE_FATAL = '!fatal'; + + /** + * @var array An array of unrecognized words in network order. + */ + protected $unrecognizedWords = array(); + + /** + * @var string The response type. + */ + private $_type; + + /** + * Extracts a new response from a communicator. + * + * @param Communicator $com The communicator from which to extract + * the new response. + * @param bool $asStream Whether to populate the argument values + * with streams instead of strings. + * @param int $sTimeout If a response is not immediatly + * available, wait this many seconds. If NULL, wait indefinetly. + * @param int $usTimeout Microseconds to add to the waiting time. + * @param Registry $reg An optional registry to sync the + * response with. + * + * @see getType() + * @see getArgument() + */ + public function __construct( + Communicator $com, + $asStream = false, + $sTimeout = 0, + $usTimeout = null, + Registry $reg = null + ) { + if (null === $reg) { + if ($com->getTransmitter()->isPersistent()) { + $old = $com->getTransmitter() + ->lock(T\Stream::DIRECTION_RECEIVE); + try { + $this->_receive($com, $asStream, $sTimeout, $usTimeout); + } catch (E $e) { + $com->getTransmitter()->lock($old, true); + throw $e; + } + $com->getTransmitter()->lock($old, true); + } else { + $this->_receive($com, $asStream, $sTimeout, $usTimeout); + } + } else { + while (null === ($response = $reg->getNextResponse())) { + $newResponse = new self($com, true, $sTimeout, $usTimeout); + $tagInfo = $reg::parseTag($newResponse->getTag()); + $newResponse->setTag($tagInfo[1]); + if (!$reg->add($newResponse, $tagInfo[0])) { + $response = $newResponse; + break; + } + } + + $this->_type = $response->_type; + $this->attributes = $response->attributes; + $this->unrecognizedWords = $response->unrecognizedWords; + $this->setTag($response->getTag()); + + if (!$asStream) { + foreach ($this->attributes as $name => $value) { + $this->setAttribute( + $name, + stream_get_contents($value) + ); + } + foreach ($response->unrecognizedWords as $i => $value) { + $this->unrecognizedWords[$i] = stream_get_contents($value); + } + } + } + } + + /** + * Extracts a new response from a communicator. + * + * This is the function that performs the actual receiving, while the + * constructor is also involved in locks and registry sync. + * + * @param Communicator $com The communicator from which to extract + * the new response. + * @param bool $asStream Whether to populate the argument values + * with streams instead of strings. + * @param int $sTimeout If a response is not immediatly + * available, wait this many seconds. If NULL, wait indefinetly. + * Note that if an empty sentence is received, the timeout will be + * reset for another sentence receiving. + * @param int $usTimeout Microseconds to add to the waiting time. + * + * @return void + */ + private function _receive( + Communicator $com, + $asStream = false, + $sTimeout = 0, + $usTimeout = null + ) { + do { + if (!$com->getTransmitter()->isDataAwaiting( + $sTimeout, + $usTimeout + )) { + throw new SocketException( + 'No data within the time limit', + SocketException::CODE_NO_DATA + ); + } + $type = $com->getNextWord(); + } while ('' === $type); + $this->setType($type); + if ($asStream) { + for ($word = $com->getNextWordAsStream(), fseek($word, 0, SEEK_END); + ftell($word) !== 0; + $word = $com->getNextWordAsStream(), fseek( + $word, + 0, + SEEK_END + )) { + rewind($word); + $ind = fread($word, 1); + if ('=' === $ind || '.' === $ind) { + $prefix = stream_get_line($word, null, '='); + } + if ('=' === $ind) { + $value = fopen('php://temp', 'r+b'); + $bytesCopied = ftell($word); + while (!feof($word)) { + $bytesCopied += stream_copy_to_stream( + $word, + $value, + 0xFFFFF, + $bytesCopied + ); + } + rewind($value); + $this->setAttribute($prefix, $value); + continue; + } + if ('.' === $ind && 'tag' === $prefix) { + $this->setTag(stream_get_contents($word, -1, -1)); + continue; + } + rewind($word); + $this->unrecognizedWords[] = $word; + } + } else { + for ($word = $com->getNextWord(); '' !== $word; + $word = $com->getNextWord()) { + if (preg_match('/^=([^=]+)=(.*)$/sS', $word, $matches)) { + $this->setAttribute($matches[1], $matches[2]); + } elseif (preg_match('/^\.tag=(.*)$/sS', $word, $matches)) { + $this->setTag($matches[1]); + } else { + $this->unrecognizedWords[] = $word; + } + } + } + } + + /** + * Sets the response type. + * + * Sets the response type. Valid values are the TYPE_* constants. + * + * @param string $type The new response type. + * + * @return $this The response object. + * @see getType() + */ + protected function setType($type) + { + switch ($type) { + case self::TYPE_FINAL: + case self::TYPE_DATA: + case self::TYPE_ERROR: + case self::TYPE_FATAL: + $this->_type = $type; + return $this; + default: + throw new UnexpectedValueException( + 'Unrecognized response type.', + UnexpectedValueException::CODE_RESPONSE_TYPE_UNKNOWN, + null, + $type + ); + } + } + + /** + * Gets the response type. + * + * @return string The response type. + * @see setType() + */ + public function getType() + { + return $this->_type; + } + + /** + * Gets the value of an argument. + * + * @param string $name The name of the argument. + * + * @return string|resource|null The value of the specified argument. + * Returns NULL if such an argument is not set. + * @deprecated 1.0.0b5 Use {@link static::getProperty()} instead. + * This method will be removed upon final release, and is currently + * left standing merely because it can't be easily search&replaced in + * existing code, due to the fact the name "getArgument()" is shared + * with {@link Request::getArgument()}, which is still valid. + * @codeCoverageIgnore + */ + public function getArgument($name) + { + trigger_error( + 'Response::getArgument() is deprecated in favor of ' . + 'Response::getProperty() (but note that Request::getArgument() ' . + 'is still valid)', + E_USER_DEPRECATED + ); + return $this->getAttribute($name); + } + + /** + * Gets the value of a property. + * + * @param string $name The name of the property. + * + * @return string|resource|null The value of the specified property. + * Returns NULL if such a property is not set. + */ + public function getProperty($name) + { + return parent::getAttribute($name); + } + + /** + * Gets a list of unrecognized words. + * + * @return array The list of unrecognized words. + */ + public function getUnrecognizedWords() + { + return $this->unrecognizedWords; + } + + /** + * Counts the number of arguments or words. + * + * @param int $mode The counter mode. + * Either COUNT_NORMAL or COUNT_RECURSIVE. + * When in normal mode, counts the number of arguments. + * When in recursive mode, counts the number of API words. + * + * @return int The number of arguments/words. + */ + public function count($mode = COUNT_NORMAL) + { + $result = parent::count($mode); + if ($mode !== COUNT_NORMAL) { + $result += count($this->unrecognizedWords); + } + return $result; + } +} diff --git a/system/autoload/PEAR2/Net/RouterOS/ResponseCollection.php b/system/autoload/PEAR2/Net/RouterOS/ResponseCollection.php new file mode 100644 index 0000000..9b27575 --- /dev/null +++ b/system/autoload/PEAR2/Net/RouterOS/ResponseCollection.php @@ -0,0 +1,569 @@ + + * @copyright 2011 Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 1.0.0b5 + * @link http://pear2.php.net/PEAR2_Net_RouterOS + */ +/** + * The namespace declaration. + */ +namespace PEAR2\Net\RouterOS; + +/** + * Implemented by this class. + */ +use ArrayAccess; + +/** + * Implemented by this class. + */ +use Countable; + +/** + * Implemented by this class. + */ +use SeekableIterator; + +/** + * Represents a collection of RouterOS responses. + * + * @category Net + * @package PEAR2_Net_RouterOS + * @author Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Net_RouterOS + * + * @method string getType() + * Calls {@link Response::getType()} + * on the response pointed by the pointer. + * @method string[] getUnrecognizedWords() + * Calls {@link Response::getUnrecognizedWords()} + * on the response pointed by the pointer. + * @method string|resource|null getProperty(string $name) + * Calls {@link Response::getProperty()} + * on the response pointed by the pointer. + * @method string getTag() + * Calls {@link Response::getTag()} + * on the response pointed by the pointer. + */ +class ResponseCollection implements ArrayAccess, SeekableIterator, Countable +{ + + /** + * @var array An array with all {@link Response} objects. + */ + protected $responses = array(); + + /** + * @var array An array with each {@link Response} object's type. + */ + protected $responseTypes = array(); + + /** + * @var array An array with each {@link Response} object's tag. + */ + protected $responseTags = array(); + + /** + * @var array An array with positions of responses, based on an property + * name. The name of each property is the array key, and the array value + * is another array where the key is the value for that property, and + * the value is the posistion of the response. For performance reasons, + * each key is built only when {@link static::setIndex()} is called with + * that property, and remains available for the lifetime of this + * collection. + */ + protected $responsesIndex = array(); + + /** + * @var array An array with all distinct properties across all + * {@link Response} objects. Created at the first call of + * {@link static::getPropertyMap()}. + */ + protected $propertyMap = null; + + /** + * @var int A pointer, as required by SeekableIterator. + */ + protected $position = 0; + + /** + * @var string|null Name of property to use as index. NULL when disabled. + */ + protected $index = null; + + /** + * @var array Criterias used by {@link compare()} to determine the order + * between two respones. See {@link orderBy()} for a detailed + * description of this array's format. + */ + protected $compareBy = array(); + + /** + * Creates a new collection. + * + * @param array $responses An array of responses, in network order. + */ + public function __construct(array $responses) + { + $pos = 0; + foreach ($responses as $response) { + if ($response instanceof Response) { + $this->responseTypes[$pos] = $response->getType(); + $this->responseTags[$pos] = $response->getTag(); + $this->responses[$pos++] = $response; + } + } + } + + /** + * A shorthand gateway. + * + * This is a magic PHP method that allows you to call the object as a + * function. Depending on the argument given, one of the other functions in + * the class is invoked and its returned value is returned by this function. + * + * @param int|string|null $offset The offset of the response to seek to. + * If the offset is negative, seek to that relative to the end. + * If the collection is indexed, you can also supply a value to seek to. + * Setting NULL will get the current response's interator. + * + * @return Response|ArrayObject The {@link Response} at the specified + * offset, the current response's iterator (which is an ArrayObject) + * when NULL is given, or FALSE if the offset is invalid + * or the collection is empty. + */ + public function __invoke($offset = null) + { + return null === $offset + ? $this->current()->getIterator() + : $this->seek($offset); + } + + /** + * Sets a property to be usable as a key in the collection. + * + * @param string|null $name The name of the property to use. Future calls + * that accept a position will then also be able to search values of + * that property for a matching value. + * Specifying NULL will disable such lookups (as is by default). + * Note that in case this value occures multiple times within the + * collection, only the last matching response will be accessible by + * that value. + * + * @return $this The object itself. + */ + public function setIndex($name) + { + if (null !== $name) { + $name = (string)$name; + if (!isset($this->responsesIndex[$name])) { + $this->responsesIndex[$name] = array(); + foreach ($this->responses as $pos => $response) { + $val = $response->getProperty($name); + if (null !== $val) { + $this->responsesIndex[$name][$val] = $pos; + } + } + } + } + $this->index = $name; + return $this; + } + + /** + * Gets the name of the property used as an index. + * + * @return string|null Name of property used as index. NULL when disabled. + */ + public function getIndex() + { + return $this->index; + } + + /** + * Gets the whole collection as an array. + * + * @param bool $useIndex Whether to use the index values as keys for the + * resulting array. + * + * @return array An array with all responses, in network order. + */ + public function toArray($useIndex = false) + { + if ($useIndex) { + $positions = $this->responsesIndex[$this->index]; + asort($positions, SORT_NUMERIC); + $positions = array_flip($positions); + return array_combine( + $positions, + array_intersect_key($this->responses, $positions) + ); + } + return $this->responses; + } + + /** + * Counts the responses/words in the collection. + * + * @param int $mode The counter mode. + * Either COUNT_NORMAL or COUNT_RECURSIVE. + * When in normal mode, counts the number of responses. + * When in recursive mode, counts the total number of API words. + * + * @return int The number of responses in the collection. + */ + public function count($mode = COUNT_NORMAL) + { + if ($mode !== COUNT_NORMAL) { + $result = 0; + foreach ($this->responses as $response) { + $result += $response->count($mode); + } + return $result; + } else { + return count($this->responses); + } + } + + /** + * Checks if an offset exists. + * + * @param int|string $offset The offset to check. If the + * collection is indexed, you can also supply a value to check. + * Note that negative numeric offsets are NOT accepted. + * + * @return bool TRUE if the offset exists, FALSE otherwise. + */ + public function offsetExists($offset) + { + return is_int($offset) + ? array_key_exists($offset, $this->responses) + : array_key_exists($offset, $this->responsesIndex[$this->index]); + } + + /** + * Gets a {@link Response} from a specified offset. + * + * @param int|string $offset The offset of the desired response. If the + * collection is indexed, you can also supply the value to search for. + * + * @return Response The response at the specified offset. + */ + public function offsetGet($offset) + { + return is_int($offset) + ? $this->responses[$offset >= 0 + ? $offset + : count($this->responses) + $offset] + : $this->responses[$this->responsesIndex[$this->index][$offset]]; + } + + /** + * N/A + * + * This method exists only because it is required for ArrayAccess. The + * collection is read only. + * + * @param int|string $offset N/A + * @param Response $value N/A + * + * @return void + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function offsetSet($offset, $value) + { + + } + + /** + * N/A + * + * This method exists only because it is required for ArrayAccess. The + * collection is read only. + * + * @param int|string $offset N/A + * + * @return void + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function offsetUnset($offset) + { + + } + + /** + * Resets the pointer to 0, and returns the first response. + * + * @return Response The first response in the collection, or FALSE if the + * collection is empty. + */ + public function rewind() + { + return $this->seek(0); + } + + /** + * Moves the position pointer to a specified position. + * + * @param int|string $position The position to move to. If the collection is + * indexed, you can also supply a value to move the pointer to. + * A non-existent index will move the pointer to "-1". + * + * @return Response The {@link Response} at the specified position, or FALSE + * if the specified position is not valid. + */ + public function seek($position) + { + $this->position = is_int($position) + ? ($position >= 0 + ? $position + : count($this->responses) + $position) + : ($this->offsetExists($position) + ? $this->responsesIndex[$this->index][$position] + : -1); + return $this->current(); + } + + /** + * Moves the pointer forward by 1, and gets the next response. + * + * @return Response The next {@link Response} object, or FALSE if the + * position is not valid. + */ + public function next() + { + ++$this->position; + return $this->current(); + } + + /** + * Gets the response at the current pointer position. + * + * @return Response The response at the current pointer position, or FALSE + * if the position is not valid. + */ + public function current() + { + return $this->valid() ? $this->responses[$this->position] : false; + } + + /** + * Moves the pointer backwards by 1, and gets the previous response. + * + * @return Response The next {@link Response} object, or FALSE if the + * position is not valid. + */ + public function prev() + { + --$this->position; + return $this->current(); + } + + /** + * Moves the pointer to the last valid position, and returns the last + * response. + * + * @return Response The last response in the collection, or FALSE if the + * collection is empty. + */ + public function end() + { + $this->position = count($this->responses) - 1; + return $this->current(); + } + + /** + * Gets the key at the current pointer position. + * + * @return int The key at the current pointer position, i.e. the pointer + * position itself, or FALSE if the position is not valid. + */ + public function key() + { + return $this->valid() ? $this->position : false; + } + + /** + * Checks if the pointer is still pointing to an existing offset. + * + * @return bool TRUE if the pointer is valid, FALSE otherwise. + */ + public function valid() + { + return $this->offsetExists($this->position); + } + + /** + * Gets all distinct property names. + * + * Gets all distinct property names across all responses. + * + * @return array An array with all distinct property names as keys, and the + * indexes at which they occur as values. + */ + public function getPropertyMap() + { + if (null === $this->propertyMap) { + $properties = array(); + foreach ($this->responses as $index => $response) { + $names = array_keys($response->getIterator()->getArrayCopy()); + foreach ($names as $name) { + if (!isset($properties[$name])) { + $properties[$name] = array(); + } + $properties[$name][] = $index; + } + } + $this->propertyMap = $properties; + } + return $this->propertyMap; + } + + /** + * Gets all responses of a specified type. + * + * @param string $type The response type to filter by. Valid values are the + * Response::TYPE_* constants. + * + * @return static A new collection with responses of the + * specified type. + */ + public function getAllOfType($type) + { + $result = array(); + foreach (array_keys($this->responseTypes, $type, true) as $index) { + $result[] = $this->responses[$index]; + } + return new static($result); + } + + /** + * Gets all responses with a specified tag. + * + * @param string $tag The tag to filter by. + * + * @return static A new collection with responses having the + * specified tag. + */ + public function getAllTagged($tag) + { + $result = array(); + foreach (array_keys($this->responseTags, $tag, true) as $index) { + $result[] = $this->responses[$index]; + } + return new static($result); + } + + /** + * Order resones by criteria. + * + * @param mixed[] $criteria The criteria to order respones by. It takes the + * form of an array where each key is the name of the property to use + * as (N+1)th sorting key. The value of each member can be either NULL + * (for that property, sort normally in ascending order), a single sort + * order constant (SORT_ASC or SORT_DESC) to sort normally in the + * specified order, an array where the first member is an order + * constant, and the second one is sorting flags (same as built in PHP + * array functions) or a callback. + * If a callback is provided, it must accept two arguments + * (the two values to be compared), and return -1, 0 or 1 if the first + * value is respectively less than, equal to or greather than the second + * one. + * Each key of $criteria can also be numeric, in which case the + * value is the name of the property, and sorting is done normally in + * ascending order. + * + * @return static A new collection with the responses sorted in the + * specified order. + */ + public function orderBy(array $criteria) + { + $this->compareBy = $criteria; + $sortedResponses = $this->responses; + usort($sortedResponses, array($this, 'compare')); + return new static($sortedResponses); + } + + /** + * Calls a method of the response pointed by the pointer. + * + * Calls a method of the response pointed by the pointer. This is a magic + * PHP method, thanks to which any function you call on the collection that + * is not defined will be redirected to the response. + * + * @param string $method The name of the method to call. + * @param array $args The arguments to pass to the method. + * + * @return mixed Whatever the called function returns. + */ + public function __call($method, array $args) + { + return call_user_func_array( + array($this->current(), $method), + $args + ); + } + + /** + * Compares two respones. + * + * Compares two respones, based on criteria defined in + * {@link static::$compareBy}. + * + * @param Response $itemA The response to compare. + * @param Response $itemB The response to compare $a against. + * + * @return int Returns 0 if the two respones are equal according to every + * criteria specified, -1 if $a should be placed before $b, and 1 if $b + * should be placed before $a. + */ + protected function compare(Response $itemA, Response $itemB) + { + foreach ($this->compareBy as $name => $spec) { + if (!is_string($name)) { + $name = $spec; + $spec = null; + } + + $members = array( + 0 => $itemA->getProperty($name), + 1 => $itemB->getProperty($name) + ); + + if (is_callable($spec)) { + uasort($members, $spec); + } elseif ($members[0] === $members[1]) { + continue; + } else { + $flags = SORT_REGULAR; + $order = SORT_ASC; + if (is_array($spec)) { + list($order, $flags) = $spec; + } elseif (null !== $spec) { + $order = $spec; + } + + if (SORT_ASC === $order) { + asort($members, $flags); + } else { + arsort($members, $flags); + } + } + return (key($members) === 0) ? -1 : 1; + } + + return 0; + } +} diff --git a/system/autoload/PEAR2/Net/RouterOS/SocketException.php b/system/autoload/PEAR2/Net/RouterOS/SocketException.php new file mode 100644 index 0000000..0beb740 --- /dev/null +++ b/system/autoload/PEAR2/Net/RouterOS/SocketException.php @@ -0,0 +1,44 @@ + + * @copyright 2011 Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 1.0.0b5 + * @link http://pear2.php.net/PEAR2_Net_RouterOS + */ +/** + * The namespace declaration. + */ +namespace PEAR2\Net\RouterOS; + +/** + * Base of this class. + */ +use RuntimeException; + +/** + * Exception thrown when something goes wrong with the connection. + * + * @category Net + * @package PEAR2_Net_RouterOS + * @author Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Net_RouterOS + */ +class SocketException extends RuntimeException implements Exception +{ + const CODE_SERVICE_INCOMPATIBLE = 10200; + const CODE_CONNECTION_FAIL = 100; + const CODE_QUERY_SEND_FAIL = 30600; + const CODE_REQUEST_SEND_FAIL = 40900; + const CODE_NO_DATA = 50000; +} diff --git a/system/autoload/PEAR2/Net/RouterOS/UnexpectedValueException.php b/system/autoload/PEAR2/Net/RouterOS/UnexpectedValueException.php new file mode 100644 index 0000000..b09ce5c --- /dev/null +++ b/system/autoload/PEAR2/Net/RouterOS/UnexpectedValueException.php @@ -0,0 +1,88 @@ + + * @copyright 2011 Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 1.0.0b5 + * @link http://pear2.php.net/PEAR2_Net_RouterOS + */ +/** + * The namespace declaration. + */ +namespace PEAR2\Net\RouterOS; + +use UnexpectedValueException as U; + +/** + * Exception thrown when encountering an invalid value in a function argument. + * + * @category Net + * @package PEAR2_Net_RouterOS + * @author Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Net_RouterOS + */ +class UnexpectedValueException extends U implements Exception +{ + const CODE_CALLBACK_INVALID = 10502; + const CODE_ACTION_UNKNOWN = 30100; + const CODE_RESPONSE_TYPE_UNKNOWN = 50100; + + /** + * @var mixed The unexpected value. + */ + private $_value; + + /** + * Creates a new UnexpectedValueException. + * + * @param string $message The Exception message to throw. + * @param int $code The Exception code. + * @param \Exception $previous The previous exception used for the exception + * chaining. + * @param mixed $value The unexpected value. + */ + public function __construct( + $message, + $code = 0, + $previous = null, + $value = null + ) { + parent::__construct($message, $code, $previous); + $this->_value = $value; + } + + /** + * Gets the unexpected value. + * + * @return mixed The unexpected value. + */ + public function getValue() + { + return $this->_value; + } + + // @codeCoverageIgnoreStart + // String representation is not reliable in testing + + /** + * Returns a string representation of the exception. + * + * @return string The exception as a string. + */ + public function __toString() + { + return parent::__toString() . "\nValue:{$this->_value}"; + } + + // @codeCoverageIgnoreEnd +} diff --git a/system/autoload/PEAR2/Net/RouterOS/Util.php b/system/autoload/PEAR2/Net/RouterOS/Util.php new file mode 100644 index 0000000..8222980 --- /dev/null +++ b/system/autoload/PEAR2/Net/RouterOS/Util.php @@ -0,0 +1,1177 @@ + + * @copyright 2011 Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 1.0.0b5 + * @link http://pear2.php.net/PEAR2_Net_RouterOS + */ +/** + * The namespace declaration. + */ +namespace PEAR2\Net\RouterOS; + +/** + * Values at {@link Util::exec()} can be casted from this type. + */ +use DateTime; + +/** + * Values at {@link Util::exec()} can be casted from this type. + */ +use DateInterval; + +/** + * Implemented by this class. + */ +use Countable; + +/** + * Used to reliably write to streams at {@link static::prepareScript()}. + */ +use PEAR2\Net\Transmitter\Stream; + +/** + * Utility class. + * + * Abstracts away frequently used functionality (particularly CRUD operations) + * in convinient to use methods by wrapping around a connection. + * + * @category Net + * @package PEAR2_Net_RouterOS + * @author Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Net_RouterOS + */ +class Util implements Countable +{ + /** + * @var Client The connection to wrap around. + */ + protected $client; + + /** + * @var string The current menu. + */ + protected $menu = '/'; + + /** + * @var array|null An array with the numbers of items in the current menu as + * keys, and the corresponding IDs as values. NULL when the cache needs + * regenerating. + */ + protected $idCache = null; + + /** + * Parses a value from a RouterOS scripting context. + * + * Turns a value from RouterOS into an equivalent PHP value, based on + * determining the type in the same way RouterOS would determine it for a + * literal. + * + * This method is intended to be the very opposite of + * {@link static::escapeValue()}. hat is, results from that method, if + * given to this method, should produce equivalent results. + * + * @param string $value The value to be parsed. Must be a literal of a + * value, e.g. what {@link static::escapeValue()} will give you. + * + * @return mixed Depending on RouterOS type detected: + * - "nil" or "nothing" - NULL. + * - "number" - int or double for large values. + * - "bool" - a boolean. + * - "time" - a {@link DateInterval} object. + * - "array" - an array, with the values processed recursively. + * - "str" - a string. + * - Unrecognized type - treated as an unquoted string. + */ + public static function parseValue($value) + { + $value = (string)$value; + + if (in_array($value, array('', 'nil'), true)) { + return null; + } elseif (in_array($value, array('true', 'false', 'yes', 'no'), true)) { + return $value === 'true' || $value === 'yes'; + } elseif ($value === (string)($num = (int)$value) + || $value === (string)($num = (double)$value) + ) { + return $num; + } elseif (preg_match( + '/^ + (?:(\d+)w)? + (?:(\d+)d)? + (?:(\d\d)\:)? + (\d\d)\: + (\d\d(:\.\d{1,6})?) + $/x', + $value, + $time + )) { + $days = isset($time[2]) ? (int)$time[2] : 0; + if (isset($time[1])) { + $days += 7 * (int)$time[1]; + } + if ('' === $time[3]) { + $time[3] = 0; + } + return new DateInterval( + "P{$days}DT{$time[3]}H{$time[4]}M{$time[5]}S" + ); + } elseif (('"' === $value[0]) && substr(strrev($value), 0, 1) === '"') { + return str_replace( + array('\"', '\\\\', "\\\n", "\\\r\n", "\\\r"), + array('"', '\\'), + substr($value, 1, -1) + ); + } elseif ('{' === $value[0]) { + $len = strlen($value); + if ($value[$len - 1] === '}') { + $value = substr($value, 1, -1); + if ('' === $value) { + return array(); + } + $parsedValue = preg_split( + '/ + (\"(?:\\\\\\\\|\\\\"|[^"])*\") + | + (\{[^{}]*(?2)?\}) + | + ([^;=]+) + /sx', + $value, + null, + PREG_SPLIT_DELIM_CAPTURE + ); + $result = array(); + $newVal = null; + $newKey = null; + for ($i = 0, $l = count($parsedValue); $i < $l; ++$i) { + switch ($parsedValue[$i]) { + case '': + break; + case ';': + if (null === $newKey) { + $result[] = $newVal; + } else { + $result[$newKey] = $newVal; + } + $newKey = $newVal = null; + break; + case '=': + $newKey = static::parseValue($parsedValue[$i - 1]); + $newVal = static::parseValue($parsedValue[++$i]); + break; + default: + $newVal = static::parseValue($parsedValue[$i]); + } + } + if (null === $newKey) { + $result[] = $newVal; + } else { + $result[$newKey] = $newVal; + } + return $result; + } + } + return $value; + } + + /** + * Prepares a script. + * + * Prepares a script for eventual execution by prepending parameters as + * variables to it. + * + * This is particularly useful when you're creating scripts that you don't + * want to execute right now (as with {@link static::exec()}, but instead + * you want to store it for later execution, perhaps by supplying it to + * "/system scheduler". + * + * @param string|resource $source The source of the script, as a string + * or stream. If a stream is provided, reading starts from the current + * position to the end of the stream, and the pointer stays at the end + * after reading is done. + * @param array $params An array of parameters to make available + * in the script as local variables. + * Variable names are array keys, and variable values are array values. + * Array values are automatically processed with + * {@link static::escapeValue()}. Streams are also supported, and are + * processed in chunks, each with + * {@link static::escapeString()}. Processing starts from the current + * position to the end of the stream, and the stream's pointer stays at + * the end after reading is done. + * + * @return resource A new PHP temporary stream with the script as contents, + * with the pointer back at the start. + * @see static::appendScript() + */ + public static function prepareScript( + $source, + array $params = array() + ) { + $resultStream = fopen('php://temp', 'r+b'); + self::appendScript($resultStream, $source, $params); + rewind($resultStream); + return $resultStream; + } + + /** + * Appends a script. + * + * Appends a script to an existing stream. + * + * @param resource $stream An existing stream to write the resulting + * script to. + * @param string|resource $source The source of the script, as a string + * or stream. If a stream is provided, reading starts from the current + * position to the end of the stream, and the pointer stays at the end + * after reading is done. + * @param array $params An array of parameters to make available + * in the script as local variables. + * Variable names are array keys, and variable values are array values. + * Array values are automatically processed with + * {@link static::escapeValue()}. Streams are also supported, and are + * processed in chunks, each with + * {@link static::escapeString()}. Processing starts from the current + * position to the end of the stream, and the stream's pointer stays at + * the end after reading is done. + * + * @return int The number of bytes written to $stream is returned, + * and the pointer remains where it was after the write + * (i.e. it is not seeked back, even if seeking is supported). + */ + public static function appendScript( + $stream, + $source, + array $params = array() + ) { + $writer = new Stream($stream, false); + $bytes = 0; + + foreach ($params as $pname => $pvalue) { + $pname = static::escapeString($pname); + $bytes += $writer->send(":local \"{$pname}\" "); + if (Stream::isStream($pvalue)) { + $reader = new Stream($pvalue, false); + $chunkSize = $reader->getChunk(Stream::DIRECTION_RECEIVE); + $bytes += $writer->send('"'); + while ($reader->isAvailable() && $reader->isDataAwaiting()) { + $bytes += $writer->send( + static::escapeString(fread($pvalue, $chunkSize)) + ); + } + $bytes += $writer->send("\";\n"); + } else { + $bytes += $writer->send(static::escapeValue($pvalue) . ";\n"); + } + } + + $bytes += $writer->send($source); + return $bytes; + } + + /** + * Escapes a value for a RouterOS scripting context. + * + * Turns any native PHP value into an equivalent whole value that can be + * inserted as part of a RouterOS script. + * + * DateTime and DateInterval objects will be casted to RouterOS' "time" + * type. A DateTime object will be converted to a time relative to the UNIX + * epoch time. Note that if a DateInterval does not have the "days" property + * ("a" in formatting), then its months and years will be ignored, because + * they can't be unambigiously converted to a "time" value. + * + * Unrecognized types (i.e. resources and other objects) are casted to + * strings. + * + * @param mixed $value The value to be escaped. + * + * @return string A string representation that can be directly inserted in a + * script as a whole value. + */ + public static function escapeValue($value) + { + switch(gettype($value)) { + case 'NULL': + $value = ''; + break; + case 'integer': + $value = (string)$value; + break; + case 'boolean': + $value = $value ? 'true' : 'false'; + break; + case 'array': + if (0 === count($value)) { + $value = '({})'; + break; + } + $result = ''; + foreach ($value as $key => $val) { + $result .= ';'; + if (!is_int($key)) { + $result .= static::escapeValue($key) . '='; + } + $result .= static::escapeValue($val); + } + $value = '{' . substr($result, 1) . '}'; + break; + case 'object': + if ($value instanceof DateTime) { + $usec = $value->format('u'); + if ('000000' === $usec) { + unset($usec); + } + $unixEpoch = new DateTime('@0'); + $value = $unixEpoch->diff($value); + } + if ($value instanceof DateInterval) { + if (false === $value->days || $value->days < 0) { + $value = $value->format('%r%dd%H:%I:%S'); + } else { + $value = $value->format('%r%ad%H:%I:%S'); + } + if (strpos('.', $value) === false && isset($usec)) { + $value .= '.' . $usec; + } + break; + } + //break; intentionally omitted + default: + $value = '"' . static::escapeString((string)$value) . '"'; + break; + } + return $value; + } + + /** + * Escapes a string for a RouterOS scripting context. + * + * Escapes a string for a RouterOS scripting context. The value can then be + * surrounded with quotes at a RouterOS script (or concatenated onto a + * larger string first), and you can be sure there won't be any code + * injections coming from it. + * + * @param string $value Value to be escaped. + * + * @return string The escaped value. + */ + public static function escapeString($value) + { + return preg_replace_callback( + '/[^\\_A-Za-z0-9]+/S', + array(__CLASS__, '_escapeCharacters'), + $value + ); + } + + /** + * Escapes a character for a RouterOS scripting context. + * + * Escapes a character for a RouterOS scripting context. Intended to only be + * called for non-alphanumeric characters. + * + * @param string $chars The matches array, expected to contain exactly one + * member, in which is the whole string to be escaped. + * + * @return string The escaped characters. + */ + private static function _escapeCharacters($chars) + { + $result = ''; + for ($i = 0, $l = strlen($chars[0]); $i < $l; ++$i) { + $result .= '\\' . str_pad( + strtoupper(dechex(ord($chars[0][$i]))), + 2, + '0', + STR_PAD_LEFT + ); + } + return $result; + } + + /** + * Creates a new Util instance. + * + * Wraps around a connection to provide convinience methods. + * + * @param Client $client The connection to wrap around. + */ + public function __construct(Client $client) + { + $this->client = $client; + } + + /** + * Gets the current menu. + * + * @return string The current menu. + */ + public function getMenu() + { + return $this->menu; + } + + /** + * Sets the current menu. + * + * Sets the current menu. + * + * @param string $newMenu The menu to change to. Can be specified with API + * or CLI syntax and can be either absolute or relative. If relative, + * it's relative to the current menu, which by default is the root. + * + * @return $this The object itself. If an empty string is given for + * a new menu, no change is performed, + * but the ID cache is cleared anyway. + * + * @see static::clearIdCache() + */ + public function setMenu($newMenu) + { + $newMenu = (string)$newMenu; + if ('' !== $newMenu) { + $menuRequest = new Request('/menu'); + if ('/' === $newMenu[0]) { + $this->menu = $menuRequest->setCommand($newMenu)->getCommand(); + } else { + $this->menu = $menuRequest->setCommand( + '/' . str_replace('/', ' ', substr($this->menu, 1)) . ' ' . + str_replace('/', ' ', $newMenu) + )->getCommand(); + } + } + $this->clearIdCache(); + return $this; + } + + /** + * Executes a RouterOS script. + * + * Executes a RouterOS script, written as a string or a stream. + * Note that in cases of errors, the line numbers will be off, because the + * script is executed at the current menu as context, with the specified + * variables pre declared. This is achieved by prepending 1+count($params) + * lines before your actual script. + * + * @param string|resource $source The source of the script, as a string or + * stream. If a stream is provided, reading starts from the current + * position to the end of the stream, and the pointer stays at the end + * after reading is done. + * @param array $params An array of parameters to make available + * in the script as local variables. + * Variable names are array keys, and variable values are array values. + * Array values are automatically processed with + * {@link static::escapeValue()}. Streams are also supported, and are + * processed in chunks, each processed with + * {@link static::escapeString()}. Processing starts from the current + * position to the end of the stream, and the stream's pointer is left + * untouched after the reading is done. + * Note that the script's (generated) name is always added as the + * variable "_", which will be inadvertently lost if you overwrite it + * from here. + * @param string $policy Allows you to specify a policy the script + * must follow. Has the same format as in terminal. + * If left NULL, the script has no restrictions beyond those imposed by + * the username. + * @param string $name The script is executed after being saved + * in "/system script" under a random name (prefixed with the computer's + * name), and is removed after execution. To eliminate any possibility + * of name clashes, you can specify your own name. + * + * @return ResponseCollection Returns the response collection of the + * run, allowing you to inspect errors, if any. + * If the script was not added successfully before execution, the + * ResponseCollection from the add attempt is going to be returned. + */ + public function exec( + $source, + array $params = array(), + $policy = null, + $name = null + ) { + return $this->_exec($source, $params, $policy, $name); + } + + /** + * Clears the ID cache. + * + * Normally, the ID cache improves performance when targeting items by a + * number. If you're using both Util's methods and other means (e.g. + * {@link Client} or {@link Util::exec()}) to add/move/remove items, the + * cache may end up being out of date. By calling this method right before + * targeting an item with a number, you can ensure number accuracy. + * + * Note that Util's {@link static::move()} and {@link static::remove()} + * methods automatically clear the cache before returning, while + * {@link static::add()} adds the new item's ID to the cache as the next + * number. A change in the menu also clears the cache. + * + * Note also that the cache is being rebuilt unconditionally every time you + * use {@link static::find()} with a callback. + * + * @return $this The Util object itself. + */ + public function clearIdCache() + { + $this->idCache = null; + return $this; + } + + /** + * Finds the IDs of items at the current menu. + * + * Finds the IDs of items based on specified criteria, and returns them as + * a comma separated string, ready for insertion at a "numbers" argument. + * + * Accepts zero or more criteria as arguments. If zero arguments are + * specified, returns all items' IDs. The value of each criteria can be a + * number (just as in Winbox), a literal ID to be included, a {@link Query} + * object, or a callback. If a callback is specified, it is called for each + * item, with the item as an argument. If it returns a true value, the + * item's ID is included in the result. Every other value is casted to a + * string. A string is treated as a comma separated values of IDs, numbers + * or callback names. Non-existent callback names are instead placed in the + * result, which may be useful in menus that accept identifiers other than + * IDs, but note that it can cause errors on other menus. + * + * @return string A comma separated list of all items matching the + * specified criteria. + */ + public function find() + { + if (func_num_args() === 0) { + if (null === $this->idCache) { + $idCache = str_replace( + ';', + ',', + $this->client->sendSync( + new Request($this->menu . '/find') + )->getProperty('ret') + ); + $this->idCache = explode(',', $idCache); + return $idCache; + } + return implode(',', $this->idCache); + } + $idList = ''; + foreach (func_get_args() as $criteria) { + if ($criteria instanceof Query) { + foreach ($this->client->sendSync( + new Request($this->menu . '/print .proplist=.id', $criteria) + ) as $response) { + $idList .= $response->getProperty('.id') . ','; + } + } elseif (is_callable($criteria)) { + $idCache = array(); + foreach ($this->client->sendSync( + new Request($this->menu . '/print') + ) as $response) { + if ($criteria($response)) { + $idList .= $response->getProperty('.id') . ','; + } + $idCache[] = $response->getProperty('.id'); + } + $this->idCache = $idCache; + } else { + $this->find(); + if (is_int($criteria)) { + if (isset($this->idCache[$criteria])) { + $idList = $this->idCache[$criteria] . ','; + } + } else { + $criteria = (string)$criteria; + if ($criteria === (string)(int)$criteria) { + if (isset($this->idCache[(int)$criteria])) { + $idList .= $this->idCache[(int)$criteria] . ','; + } + } elseif (false === strpos($criteria, ',')) { + $idList .= $criteria . ','; + } else { + $criteriaArr = explode(',', $criteria); + for ($i = count($criteriaArr) - 1; $i >= 0; --$i) { + if ('' === $criteriaArr[$i]) { + unset($criteriaArr[$i]); + } elseif ('*' === $criteriaArr[$i][0]) { + $idList .= $criteriaArr[$i] . ','; + unset($criteriaArr[$i]); + } + } + if (!empty($criteriaArr)) { + $idList .= call_user_func_array( + array($this, 'find'), + $criteriaArr + ) . ','; + } + } + } + } + } + return rtrim($idList, ','); + } + + /** + * Gets a value of a specified item at the current menu. + * + * @param int|string|null $number A number identifying the item you're + * targeting. Can also be an ID or (in some menus) name. For menus where + * there are no items (e.g. "/system identity"), you can specify NULL. + * @param string $valueName The name of the value you want to get. + * + * @return string|null|bool The value of the specified property. If the + * property is not set, NULL will be returned. FALSE on failure + * (e.g. no such item, invalid property, etc.). + */ + public function get($number, $valueName) + { + if (is_int($number) || ((string)$number === (string)(int)$number)) { + $this->find(); + if (isset($this->idCache[(int)$number])) { + $number = $this->idCache[(int)$number]; + } else { + return false; + } + } + + //For new RouterOS versions + $request = new Request($this->menu . '/get'); + $request->setArgument('number', $number); + $request->setArgument('value-name', $valueName); + $responses = $this->client->sendSync($request); + if (Response::TYPE_ERROR === $responses->getType()) { + return false; + } + $result = $responses->getProperty('ret'); + if (null !== $result) { + return $result; + } + + // The "get" of old RouterOS versions returns an empty !done response. + // New versions return such only when the property is not set. + // This is a backup for old versions' sake. + $query = null; + if (null !== $number) { + $number = (string)$number; + $query = Query::where('.id', $number)->orWhere('name', $number); + } + $responses = $this->getAll( + array('.proplist' => $valueName, 'detail'), + $query + ); + + if (0 === count($responses)) { + // @codeCoverageIgnoreStart + // New versions of RouterOS can't possibly reach this section. + return false; + // @codeCoverageIgnoreEnd + } + return $responses->getProperty($valueName); + } + + /** + * Enables all items at the current menu matching certain criteria. + * + * Zero or more arguments can be specified, each being a criteria. + * If zero arguments are specified, enables all items. + * See {@link static::find()} for a description of what criteria are + * accepted. + * + * @return ResponseCollection returns the response collection, allowing you + * to inspect errors, if any. + */ + public function enable() + { + return $this->doBulk('enable', func_get_args()); + } + + /** + * Disables all items at the current menu matching certain criteria. + * + * Zero or more arguments can be specified, each being a criteria. + * If zero arguments are specified, disables all items. + * See {@link static::find()} for a description of what criteria are + * accepted. + * + * @return ResponseCollection Returns the response collection, allowing you + * to inspect errors, if any. + */ + public function disable() + { + return $this->doBulk('disable', func_get_args()); + } + + /** + * Removes all items at the current menu matching certain criteria. + * + * Zero or more arguments can be specified, each being a criteria. + * If zero arguments are specified, removes all items. + * See {@link static::find()} for a description of what criteria are + * accepted. + * + * @return ResponseCollection Returns the response collection, allowing you + * to inspect errors, if any. + */ + public function remove() + { + $result = $this->doBulk('remove', func_get_args()); + $this->clearIdCache(); + return $result; + } + + /** + * Sets new values. + * + * Sets new values on certain properties on all items at the current menu + * which match certain criteria. + * + * @param mixed $numbers Targeted items. Can be any criteria accepted by + * {@link static::find()} or NULL in case the menu is one without items + * (e.g. "/system identity"). + * @param array $newValues An array with the names of each property to set + * as an array key, and the new value as an array value. + * Flags (properties with a value "true" that is interpreted as + * equivalent of "yes" from CLI) can also be specified with a numeric + * index as the array key, and the name of the flag as the array value. + * + * @return ResponseCollection Returns the response collection, allowing you + * to inspect errors, if any. + */ + public function set($numbers, array $newValues) + { + $setRequest = new Request($this->menu . '/set'); + foreach ($newValues as $name => $value) { + if (is_int($name)) { + $setRequest->setArgument($value, 'true'); + } else { + $setRequest->setArgument($name, $value); + } + } + if (null !== $numbers) { + $setRequest->setArgument('numbers', $this->find($numbers)); + } + return $this->client->sendSync($setRequest); + } + + /** + * Alias of {@link static::set()} + * + * @param mixed $numbers Targeted items. Can be any criteria accepted by + * {@link static::find()}. + * @param array $newValues An array with the names of each changed property + * as an array key, and the new value as an array value. + * Flags (properties with a value "true" that is interpreted as + * equivalent of "yes" from CLI) can also be specified with a numeric + * index as the array key, and the name of the flag as the array value. + * + * @return ResponseCollection Returns the response collection, allowing you + * to inspect errors, if any. + */ + public function edit($numbers, array $newValues) + { + return $this->set($numbers, $newValues); + } + + /** + * Unsets a value of a specified item at the current menu. + * + * Equivalent of scripting's "unset" command. The "Value" part in the method + * name is added because "unset" is a language construct, and thus a + * reserved word. + * + * @param mixed $numbers Targeted items. Can be any criteria accepted + * by {@link static::find()}. + * @param string $valueName The name of the value you want to unset. + * + * @return ResponseCollection Returns the response collection, allowing you + * to inspect errors, if any. + */ + public function unsetValue($numbers, $valueName) + { + $unsetRequest = new Request($this->menu . '/unset'); + return $this->client->sendSync( + $unsetRequest->setArgument('numbers', $this->find($numbers)) + ->setArgument('value-name', $valueName) + ); + } + + /** + * Adds a new item at the current menu. + * + * @param array $values Accepts one or more items to add to the + * current menu. The data about each item is specified as an array with + * the names of each property as an array key, and the value as an array + * value. + * Flags (properties with a value "true" that is interpreted as + * equivalent of "yes" from CLI) can also be specified with a numeric + * index as the array key, and the name of the flag as the array value. + * @param array $... Additional items. + * + * @return string A comma separated list of the new items' IDs. If a + * particular item was not added, this will be indicated by an empty + * string in its spot on the list. e.g. "*1D,,*1E" means that + * you supplied three items to be added, of which the second one was + * not added for some reason. + */ + public function add(array $values) + { + $addRequest = new Request($this->menu . '/add'); + $idList = ''; + foreach (func_get_args() as $values) { + $idList .= ','; + if (!is_array($values)) { + continue; + } + foreach ($values as $name => $value) { + if (is_int($name)) { + $addRequest->setArgument($value, 'true'); + } else { + $addRequest->setArgument($name, $value); + } + } + $id = $this->client->sendSync($addRequest)->getProperty('ret'); + if (null !== $this->idCache) { + $this->idCache[] = $id; + } + $idList .= $id; + $addRequest->removeAllArguments(); + } + return substr($idList, 1); + } + + /** + * Moves items at the current menu before a certain other item. + * + * Moves items before a certain other item. Note that the "move" + * command is not available on all menus. As a rule of thumb, if the order + * of items in a menu is irrelevant to their interpretation, there won't + * be a move command on that menu. If in doubt, check from a terminal. + * + * @param mixed $numbers Targeted items. Can be any criteria accepted + * by {@link static::find()}. + * @param mixed $destination item before which the targeted items will be + * moved to. Can be any criteria accepted by {@link static::find()}. + * If multiple items match the criteria, the targeted items will move + * above the first match. + * + * @return ResponseCollection Returns the response collection, allowing you + * to inspect errors, if any. + */ + public function move($numbers, $destination) + { + $moveRequest = new Request($this->menu . '/move'); + $moveRequest->setArgument('numbers', $this->find($numbers)); + $destination = $this->find($destination); + if (false !== strpos($destination, ',')) { + $destination = strstr($destination, ',', true); + } + $moveRequest->setArgument('destination', $destination); + $this->clearIdCache(); + return $this->client->sendSync($moveRequest); + } + + /** + * Counts items at the current menu. + * + * Counts items at the current menu. This executes a dedicated command + * ("print" with a "count-only" argument) on RouterOS, which is why only + * queries are allowed as a criteria, in contrast with + * {@link static::find()}, where numbers and callbacks are allowed also. + * + * @param int $mode The counter mode. + * Currently ignored, but present for compatiblity with PHP 5.6+. + * @param Query $query A query to filter items by. Without it, all items + * are included in the count. + * + * @return int The number of items, or -1 on failure (e.g. if the + * current menu does not have a "print" command or items to be counted). + */ + public function count($mode = COUNT_NORMAL, Query $query = null) + { + $result = $this->client->sendSync( + new Request($this->menu . '/print count-only=""', $query) + )->end()->getProperty('ret'); + + if (null === $result) { + return -1; + } + if (Stream::isStream($result)) { + $result = stream_get_contents($result); + } + return (int)$result; + } + + /** + * Gets all items in the current menu. + * + * Gets all items in the current menu, using a print request. + * + * @param array $args Additional arguments to pass + * to the request. + * Each array key is the name of the argument, and each array value is + * the value of the argument to be passed. + * Arguments without a value (i.e. empty arguments) can also be + * specified using a numeric key, and the name of the argument as the + * array value. + * @param Query|null $query A query to filter items by. + * NULL to get all items. + * + * @return ResponseCollection|false A response collection with all + * {@link Response::TYPE_DATA} responses. The collection will be empty + * when there are no matching items. FALSE on failure. + */ + public function getAll(array $args = array(), Query $query = null) + { + $printRequest = new Request($this->menu . '/print', $query); + foreach ($args as $name => $value) { + if (is_int($name)) { + $printRequest->setArgument($value); + } else { + $printRequest->setArgument($name, $value); + } + } + $responses = $this->client->sendSync($printRequest); + + if (count($responses->getAllOfType(Response::TYPE_ERROR)) > 0) { + return false; + } + return $responses->getAllOfType(Response::TYPE_DATA); + } + + /** + * Puts a file on RouterOS's file system. + * + * Puts a file on RouterOS's file system, regardless of the current menu. + * Note that this is a **VERY VERY VERY** time consuming method - it takes a + * minimum of a little over 4 seconds, most of which are in sleep. It waits + * 2 seconds after a file is first created (required to actually start + * writing to the file), and another 2 seconds after its contents is written + * (performed in order to verify success afterwards). + * Similarly for removal (when $data is NULL) - there are two seconds in + * sleep, used to verify the file was really deleted. + * + * If you want an efficient way of transferring files, use (T)FTP. + * If you want an efficient way of removing files, use + * {@link static::setMenu()} to move to the "/file" menu, and call + * {@link static::remove()} without performing verification afterwards. + * + * @param string $filename The filename to write data in. + * @param string|resource|null $data The data the file is going to have + * as a string or a seekable stream. + * Setting the value to NULL removes a file of this name. + * If a seekable stream is provided, it is sent from its current + * posistion to its end, and the pointer is seeked back to its current + * position after sending. + * Non seekable streams, as well as all other types, are casted to a + * string. + * @param bool $overwrite Whether to overwrite the file if + * it exists. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function filePutContents($filename, $data, $overwrite = false) + { + $printRequest = new Request( + '/file/print .proplist=""', + Query::where('name', $filename) + ); + $fileExists = count($this->client->sendSync($printRequest)) > 1; + + if (null === $data) { + if (!$fileExists) { + return false; + } + $removeRequest = new Request('/file/remove'); + $this->client->sendSync( + $removeRequest->setArgument('numbers', $filename) + ); + //Required for RouterOS to REALLY remove the file. + sleep(2); + return !(count($this->client->sendSync($printRequest)) > 1); + } + + if (!$overwrite && $fileExists) { + return false; + } + $result = $this->client->sendSync( + $printRequest->setArgument('file', $filename) + ); + if (count($result->getAllOfType(Response::TYPE_ERROR)) > 0) { + return false; + } + //Required for RouterOS to write the initial file. + sleep(2); + $setRequest = new Request('/file/set contents=""'); + $setRequest->setArgument('numbers', $filename); + $this->client->sendSync($setRequest); + $this->client->sendSync($setRequest->setArgument('contents', $data)); + //Required for RouterOS to write the file's new contents. + sleep(2); + + $fileSize = $this->client->sendSync( + $printRequest->setArgument('file', null) + ->setArgument('.proplist', 'size') + )->getProperty('size'); + if (Stream::isStream($fileSize)) { + $fileSize = stream_get_contents($fileSize); + } + if (Communicator::isSeekableStream($data)) { + return Communicator::seekableStreamLength($data) == $fileSize; + } else { + return sprintf('%u', strlen((string)$data)) === $fileSize; + }; + } + + /** + * Gets the contents of a specified file. + * + * @param string $filename The name of the file to get the contents of. + * @param string $tmpScriptName In order to get the file's contents, a + * script is created at "/system script" with a random name, the + * source of which is then overwriten with the file's contents, and + * finally retrieved. To eliminate any possibility of name clashes, you + * can specify your own name for the script. + * + * @return string|resource|false The contents of the file as a string or as + * new PHP temp stream if the underliying + * {@link Client::isStreamingResponses()} is set to TRUE. + * FALSE is returned if there is no such file. + */ + public function fileGetContents($filename, $tmpScriptName = null) + { + $checkRequest = new Request( + '/file/print', + Query::where('name', $filename) + ); + if (1 === count($this->client->sendSync($checkRequest))) { + return false; + } + $contents = $this->_exec( + '/system script set $"_" source=[/file get $filename contents]', + array('filename' => $filename), + null, + $tmpScriptName, + true + ); + return $contents; + } + + /** + * Performs an action on a bulk of items at the current menu. + * + * @param string $what What action to perform. + * @param array $args Zero or more arguments can be specified, each being + * a criteria. If zero arguments are specified, removes all items. + * See {@link static::find()} for a description of what criteria are + * accepted. + * + * @return ResponseCollection Returns the response collection, allowing you + * to inspect errors, if any. + */ + protected function doBulk($what, array $args = array()) + { + $bulkRequest = new Request($this->menu . '/' . $what); + $bulkRequest->setArgument( + 'numbers', + call_user_func_array(array($this, 'find'), $args) + ); + return $this->client->sendSync($bulkRequest); + } + + /** + * Executes a RouterOS script. + * + * Same as the public equivalent, with the addition of allowing you to get + * the contents of the script post execution, instead of removing it. + * + * @param string|resource $source The source of the script, as a string or + * stream. If a stream is provided, reading starts from the current + * position to the end of the stream, and the pointer stays at the end + * after reading is done. + * @param array $params An array of parameters to make available + * in the script as local variables. + * Variable names are array keys, and variable values are array values. + * Array values are automatically processed with + * {@link static::escapeValue()}. Streams are also supported, and are + * processed in chunks, each processed with + * {@link static::escapeString()}. Processing starts from the current + * position to the end of the stream, and the stream's pointer is left + * untouched after the reading is done. + * Note that the script's (generated) name is always added as the + * variable "_", which will be inadvertently lost if you overwrite it + * from here. + * @param string $policy Allows you to specify a policy the script + * must follow. Has the same format as in terminal. + * If left NULL, the script has no restrictions beyond those imposed by + * the username. + * @param string $name The script is executed after being saved + * in "/system script" under a random name (prefixed with the computer's + * name), and is removed after execution. To eliminate any possibility + * of name clashes, you can specify your own name. + * @param bool $get Whether to get the source of the script. + * + * @return ResponseCollection|string Returns the response collection of the + * run, allowing you to inspect errors, if any. + * If the script was not added successfully before execution, the + * ResponseCollection from the add attempt is going to be returned. + * If $get is TRUE, returns the source of the script on success. + */ + private function _exec( + $source, + array $params = array(), + $policy = null, + $name = null, + $get = false + ) { + $request = new Request('/system/script/add'); + if (null === $name) { + $name = uniqid(gethostname(), true); + } + $request->setArgument('name', $name); + $request->setArgument('policy', $policy); + + $params += array('_' => $name); + + $finalSource = fopen('php://temp', 'r+b'); + fwrite( + $finalSource, + '/' . str_replace('/', ' ', substr($this->menu, 1)). "\n" + ); + static::appendScript($finalSource, $source, $params); + fwrite($finalSource, "\n"); + rewind($finalSource); + + $request->setArgument('source', $finalSource); + $result = $this->client->sendSync($request); + + if (0 === count($result->getAllOfType(Response::TYPE_ERROR))) { + $request = new Request('/system/script/run'); + $request->setArgument('number', $name); + $result = $this->client->sendSync($request); + + if ($get) { + $result = $this->client->sendSync( + new Request( + '/system/script/print .proplist="source"', + Query::where('name', $name) + ) + )->getProperty('source'); + } + $request = new Request('/system/script/remove'); + $request->setArgument('numbers', $name); + $this->client->sendSync($request); + } + + return $result; + } +} diff --git a/system/autoload/PEAR2/Net/Transmitter/Exception.php b/system/autoload/PEAR2/Net/Transmitter/Exception.php new file mode 100644 index 0000000..e6f37b2 --- /dev/null +++ b/system/autoload/PEAR2/Net/Transmitter/Exception.php @@ -0,0 +1,36 @@ + + * @copyright 2011 Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 1.0.0a5 + * @link http://pear2.php.net/PEAR2_Net_Transmitter + */ +/** + * The namespace declaration. + */ +namespace PEAR2\Net\Transmitter; + +/** + * Generic exception class of this package. + * + * @category Net + * @package PEAR2_Net_Transmitter + * @author Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Net_Transmitter + */ +interface Exception +{ +} diff --git a/system/autoload/PEAR2/Net/Transmitter/FilterCollection.php b/system/autoload/PEAR2/Net/Transmitter/FilterCollection.php new file mode 100644 index 0000000..66f299a --- /dev/null +++ b/system/autoload/PEAR2/Net/Transmitter/FilterCollection.php @@ -0,0 +1,229 @@ + + * @copyright 2011 Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 1.0.0a5 + * @link http://pear2.php.net/PEAR2_Net_Transmitter + */ +/** + * The namespace declaration. + */ +namespace PEAR2\Net\Transmitter; + +/** + * A filter collection. + * + * Represents a collection of stream filters. + * + * @category Net + * @package PEAR2_Net_Transmitter + * @author Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Net_Transmitter + * @see Client + */ +class FilterCollection implements \SeekableIterator, \Countable +{ + /** + * @var array The filter collection itself. + */ + protected $filters = array(); + + /** + * @var int A pointer, as required by SeekableIterator. + */ + protected $position = 0; + + /** + * Appends a filter to the collection + * + * @param string $name The name of the filter. + * @param array $params An array of parameters for the filter. + * + * @return $this The collection itself. + */ + public function append($name, array $params = array()) + { + $this->filters[] = array((string) $name, $params); + return $this; + } + + /** + * Inserts the filter before a position. + * + * Inserts the specified filter before a filter at a specified position. The + * new filter takes the specified position, while previous filters are moved + * forward by one. + * + * @param int $position The position before which the filter will be + * inserted. + * @param string $name The name of the filter. + * @param array $params An array of parameters for the filter. + * + * @return $this The collection itself. + */ + public function insertBefore($position, $name, array $params = array()) + { + $position = (int) $position; + if ($position <= 0) { + $this->filters = array_merge( + array(0 => array((string) $name, $params)), + $this->filters + ); + return $this; + } + if ($position > count($this->filters)) { + return $this->append($name, $params); + } + $this->filters = array_merge( + array_slice($this->filters, 0, $position), + array(0 => array((string) $name, $params)), + array_slice($this->filters, $position) + ); + return $this; + } + + /** + * Removes a filter at a specified position. + * + * @param int $position The position from which to remove a filter. + * + * @return $this The collection itself. + */ + public function removeAt($position) + { + unset($this->filters[$position]); + $this->filters = array_values($this->filters); + return $this; + } + + /** + * Clears the collection + * + * @return $this The collection itself. + */ + public function clear() + { + $this->filters = array(); + return $this; + } + + /** + * Gets the number of filters in the collection. + * + * @return int The number of filters in the collection. + */ + public function count() + { + return count($this->filters); + } + + /** + * Resets the pointer to 0. + * + * @return bool TRUE if the collection is not empty, FALSE otherwise. + */ + public function rewind() + { + return $this->seek(0); + } + + /** + * Moves the pointer to a specified position. + * + * @param int $position The position to move to. + * + * @return bool TRUE if the specified position is valid, FALSE otherwise. + */ + public function seek($position) + { + $this->position = $position; + return $this->valid(); + } + + /** + * Gets the current position. + * + * @return int The current position. + */ + public function getCurrentPosition() + { + return $this->position; + } + + /** + * Moves the pointer forward by 1. + * + * @return bool TRUE if the new position is valid, FALSE otherwise. + */ + public function next() + { + ++$this->position; + return $this->valid(); + } + + /** + * Gets the filter name at the current pointer position. + * + * @return string The name of the filter at the current position. + */ + public function key() + { + return $this->valid() ? $this->filters[$this->position][0] : false; + } + + /** + * Gets the filter parameters at the current pointer position. + * + * @return array An array of parameters for the filter at the current + * position. + */ + public function current() + { + return $this->valid() ? $this->filters[$this->position][1] : false; + } + + /** + * Moves the pointer backwards by 1. + * + * @return bool TRUE if the new position is valid, FALSE otherwise. + */ + public function prev() + { + --$this->position; + return $this->valid(); + } + + /** + * Moves the pointer to the last valid position. + * + * @return bool TRUE if the collection is not empty, FALSE otherwise. + */ + public function end() + { + $this->position = count($this->filters) - 1; + return $this->valid(); + } + + /** + * Checks if the pointer is still pointing to an existing offset. + * + * @return bool TRUE if the pointer is valid, FALSE otherwise. + */ + public function valid() + { + return array_key_exists($this->position, $this->filters); + } +} diff --git a/system/autoload/PEAR2/Net/Transmitter/LockException.php b/system/autoload/PEAR2/Net/Transmitter/LockException.php new file mode 100644 index 0000000..afbc279 --- /dev/null +++ b/system/autoload/PEAR2/Net/Transmitter/LockException.php @@ -0,0 +1,36 @@ + + * @copyright 2011 Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 1.0.0a5 + * @link http://pear2.php.net/PEAR2_Net_Transmitter + */ +/** + * The namespace declaration. + */ +namespace PEAR2\Net\Transmitter; + +/** + * Exception thrown when something goes wrong when dealing with locks. + * + * @category Net + * @package PEAR2_Net_Transmitter + * @author Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Net_Transmitter + */ +class LockException extends \RuntimeException implements Exception +{ +} diff --git a/system/autoload/PEAR2/Net/Transmitter/NetworkStream.php b/system/autoload/PEAR2/Net/Transmitter/NetworkStream.php new file mode 100644 index 0000000..e7aec87 --- /dev/null +++ b/system/autoload/PEAR2/Net/Transmitter/NetworkStream.php @@ -0,0 +1,181 @@ + + * @copyright 2011 Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 1.0.0a5 + * @link http://pear2.php.net/PEAR2_Net_Transmitter + */ +/** + * The namespace declaration. + */ +namespace PEAR2\Net\Transmitter; + +/** + * A network transmitter. + * + * This is a convinience wrapper for network streams. Used to ensure data + * integrity. + * + * @category Net + * @package PEAR2_Net_Transmitter + * @author Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Net_Transmitter + */ +abstract class NetworkStream extends Stream +{ + /** + * Used in {@link setCrypto()} to disable encryption. + */ + const CRYPTO_OFF = ''; + + /** + * Used in {@link setCrypto()} to set encryption to either SSLv2 or SSLv3, + * depending on what the other end supports. + */ + const CRYPTO_SSL = 'SSLv23'; + + /** + * Used in {@link setCrypto()} to set encryption to SSLv2. + */ + const CRYPTO_SSL2 = 'SSLv2'; + + /** + * Used in {@link setCrypto()} to set encryption to SSLv3. + */ + const CRYPTO_SSL3 = 'SSLv3'; + + /** + * Used in {@link setCrypto()} to set encryption to TLS (exact version + * negotiated between 1.0 and 1.2). + */ + const CRYPTO_TLS = 'TLS'; + + /** + * @var string The type of stream. Can be either "_CLIENT" or "_SERVER". + * Used to complement the encryption type. Must be set by child classes + * for {@link setCrypto()} to work properly. + */ + protected $streamType = ''; + + /** + * @var string The current cryptography setting. + */ + protected $crypto = ''; + + /** + * Wraps around the specified stream. + * + * @param resource $stream The stream to wrap around. + */ + public function __construct($stream) + { + parent::__construct($stream, true); + } + + /** + * Gets the current cryptography setting. + * + * @return string One of this class' CRYPTO_* constants. + */ + public function getCrypto() + { + return $this->crypto; + } + + /** + * Sets the current connection's cryptography setting. + * + * @param string $type The encryption type to set. Must be one of this + * class' CRYPTO_* constants. + * + * @return boolean TRUE on success, FALSE on failure. + */ + public function setCrypto($type) + { + if (self::CRYPTO_OFF === $type) { + $result = stream_socket_enable_crypto($this->stream, false); + } else { + $result = stream_socket_enable_crypto( + $this->stream, + true, + constant("STREAM_CRYPTO_METHOD_{$type}{$this->streamType}") + ); + } + + if ($result) { + $this->crypto = $type; + } + return $result; + } + + /** + * Checks whether the stream is available for operations. + * + * @return bool TRUE if the stream is available, FALSE otherwise. + */ + public function isAvailable() + { + if (parent::isStream($this->stream)) { + if ($this->isBlocking && feof($this->stream)) { + return false; + } + $meta = stream_get_meta_data($this->stream); + return !$meta['eof']; + } + return false; + } + + /** + * Sets the size of a stream's buffer. + * + * @param int $size The desired size of the buffer, in bytes. + * @param string $direction The buffer of which direction to set. Valid + * values are the DIRECTION_* constants. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function setBuffer($size, $direction = self::DIRECTION_ALL) + { + $result = parent::setBuffer($size, $direction); + if (self::DIRECTION_SEND === $direction + && function_exists('stream_set_chunk_size') && !$result + ) { + return false !== @stream_set_chunk_size($this->stream, $size); + } + return $result; + } + + /** + * Shutdown a full-duplex connection + * + * Shutdowns (partially or not) a full-duplex connection. + * + * @param string $direction The direction for which to disable further + * communications. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function shutdown($direction = self::DIRECTION_ALL) + { + $directionMap = array( + self::DIRECTION_ALL => STREAM_SHUT_RDWR, + self::DIRECTION_SEND => STREAM_SHUT_WR, + self::DIRECTION_RECEIVE => STREAM_SHUT_RD + ); + return array_key_exists($direction, $directionMap) + && stream_socket_shutdown($this->stream, $directionMap[$direction]); + } +} diff --git a/system/autoload/PEAR2/Net/Transmitter/SocketException.php b/system/autoload/PEAR2/Net/Transmitter/SocketException.php new file mode 100644 index 0000000..69cbe0e --- /dev/null +++ b/system/autoload/PEAR2/Net/Transmitter/SocketException.php @@ -0,0 +1,124 @@ + + * @copyright 2011 Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 1.0.0a5 + * @link http://pear2.php.net/PEAR2_Net_Transmitter + */ +/** + * The namespace declaration. + */ +namespace PEAR2\Net\Transmitter; + +/** + * Used to enable any exception in chaining. + */ +use Exception as E; + +/** + * Exception thrown when something goes wrong with the connection. + * + * @category Net + * @package PEAR2_Net_Transmitter + * @author Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Net_Transmitter + */ +class SocketException extends StreamException +{ + + /** + * @var int The system level error code. + */ + protected $errorNo; + + /** + * @var string The system level error message. + */ + protected $errorStr; + + /** + * Creates a new socket exception. + * + * @param string $message The Exception message to throw. + * @param int $code The Exception code. + * @param E|null $previous Previous exception thrown, + * or NULL if there is none. + * @param int|string|resource|null $fragment The fragment up until the + * point of failure. + * On failure with sending, this is the number of bytes sent + * successfully before the failure. + * On failure when receiving, this is a string/stream holding + * the contents received successfully before the failure. + * NULL if the failure occured before the operation started. + * @param int $errorNo The system level error number. + * @param string $errorStr The system level + * error message. + */ + public function __construct( + $message = '', + $code = 0, + E $previous = null, + $fragment = null, + $errorNo = null, + $errorStr = null + ) { + parent::__construct($message, $code, $previous, $fragment); + $this->errorNo = $errorNo; + $this->errorStr = $errorStr; + } + + /** + * Gets the system level error code on the socket. + * + * @return int The system level error number. + */ + public function getSocketErrorNumber() + { + return $this->errorNo; + } + + // @codeCoverageIgnoreStart + // Unreliable in testing. + + /** + * Gets the system level error message on the socket. + * + * @return string The system level error message. + */ + public function getSocketErrorMessage() + { + return $this->errorStr; + } + + /** + * Returns a string representation of the exception. + * + * @return string The exception as a string. + */ + public function __toString() + { + $result = parent::__toString(); + if (null !== $this->getSocketErrorNumber()) { + $result .= "\nSocket error number:" . $this->getSocketErrorNumber(); + } + if (null !== $this->getSocketErrorMessage()) { + $result .= "\nSocket error message:" + . $this->getSocketErrorMessage(); + } + return $result; + } + // @codeCoverageIgnoreEnd +} diff --git a/system/autoload/PEAR2/Net/Transmitter/Stream.php b/system/autoload/PEAR2/Net/Transmitter/Stream.php new file mode 100644 index 0000000..bbe6829 --- /dev/null +++ b/system/autoload/PEAR2/Net/Transmitter/Stream.php @@ -0,0 +1,605 @@ + + * @copyright 2011 Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 1.0.0a5 + * @link http://pear2.php.net/PEAR2_Net_Transmitter + */ +/** + * The namespace declaration. + */ +namespace PEAR2\Net\Transmitter; + +use Exception as E; + +/** + * A stream transmitter. + * + * This is a convinience wrapper for stream functionality. Used to ensure data + * integrity. Designed for TCP sockets, but it has intentionally been made to + * accept any stream. + * + * @category Net + * @package PEAR2_Net_Transmitter + * @author Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Net_Transmitter + */ +class Stream +{ + /** + * Used to stop settings in either direction being applied. + */ + const DIRECTION_NONE = 0; + /** + * Used to apply settings only to receiving. + */ + const DIRECTION_RECEIVE = 1; + /** + * Used to apply settings only to sending. + */ + const DIRECTION_SEND = 2; + /** + * Used to apply settings to both sending and receiving. + */ + const DIRECTION_ALL = 3; + + /** + * @var resource The stream to wrap around. + */ + protected $stream; + + /** + * @var bool Whether to automaticaly close the stream on + * object destruction if it's not a persistent one. Setting this to + * FALSE may be useful if you're only using this class "part time", + * while setting it to TRUE might be useful if you're doing some + * "on offs". + */ + protected $autoClose = false; + + /** + * @var bool A flag that tells whether or not the stream is persistent. + */ + protected $persist; + + /** + * @var bool Whether the wrapped stream is in blocking mode or not. + */ + protected $isBlocking = true; + + /** + * @var array An associative array with the chunk size of each direction. + * Key is the direction, value is the size in bytes as integer. + */ + protected $chunkSize = array( + self::DIRECTION_SEND => 0xFFFFF, self::DIRECTION_RECEIVE => 0xFFFFF + ); + + /** + * Wraps around the specified stream. + * + * @param resource $stream The stream to wrap around. + * @param bool $autoClose Whether to automaticaly close the stream on + * object destruction if it's not a persistent one. Setting this to + * FALSE may be useful if you're only using this class "part time", + * while setting it to TRUE might be useful if you're doing some + * "on offs". + * + * @see static::isFresh() + */ + public function __construct($stream, $autoClose = false) + { + if (!self::isStream($stream)) { + throw $this->createException('Invalid stream supplied.', 1); + } + $this->stream = $stream; + $this->autoClose = (bool) $autoClose; + $this->persist = (bool) preg_match( + '#\s?persistent\s?#sm', + get_resource_type($stream) + ); + $meta = stream_get_meta_data($stream); + $this->isBlocking = isset($meta['blocked']) ? $meta['blocked'] : true; + } + + /** + * PHP error handler for connection errors. + * + * @param string $level Level of PHP error raised. Ignored. + * @param string $message Message raised by PHP. + * + * @return void + * @throws SocketException That's how the error is handled. + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + protected function handleError($level, $message) + { + throw $this->createException($message, 0); + } + + /** + * Checks if a given variable is a stream resource. + * + * @param mixed $var The variable to check. + * + * @return bool TRUE on success, FALSE on failure. + */ + public static function isStream($var) + { + return is_resource($var) + && (bool) preg_match('#\s?stream$#sm', get_resource_type($var)); + } + + /** + * Checks whether the wrapped stream is fresh. + * + * Checks whether the wrapped stream is fresh. A stream is considered fresh + * if there hasn't been any activity on it. Particularly useful for + * detecting reused persistent connections. + * + * @return bool TRUE if the socket is fresh, FALSE otherwise. + */ + public function isFresh() + { + return ftell($this->stream) === 0; + } + + /** + * Checks whether the wrapped stream is a persistent one. + * + * @return bool TRUE if the stream is a persistent one, FALSE otherwise. + */ + public function isPersistent() + { + return $this->persist; + } + + /** + * Checks whether the wrapped stream is a blocking one. + * + * @return bool TRUE if the stream is a blocking one, FALSE otherwise. + */ + public function isBlocking() + { + return $this->isBlocking; + } + + /** + * Sets blocking mode. + * + * @param bool $block Sets whether the stream is in blocking mode. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function setIsBlocking($block) + { + $block = (bool)$block; + if (stream_set_blocking($this->stream, (int)$block)) { + $this->isBlocking = $block; + return true; + } + return false; + } + + /** + * Sets the timeout for the stream. + * + * @param int $seconds Timeout in seconds. + * @param int $microseconds Timeout in microseconds to be added to the + * seconds. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function setTimeout($seconds, $microseconds = 0) + { + return stream_set_timeout($this->stream, $seconds, $microseconds); + } + + /** + * Sets the size of a stream's buffer. + * + * @param int $size The desired size of the buffer, in bytes. + * @param string $direction The buffer of which direction to set. Valid + * values are the DIRECTION_* constants. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function setBuffer($size, $direction = self::DIRECTION_ALL) + { + switch($direction) { + case self::DIRECTION_SEND: + return stream_set_write_buffer($this->stream, $size) === 0; + case self::DIRECTION_RECEIVE: + return stream_set_read_buffer($this->stream, $size) === 0; + case self::DIRECTION_ALL: + return $this->setBuffer($size, self::DIRECTION_RECEIVE) + && $this->setBuffer($size, self::DIRECTION_SEND); + } + return false; + } + + /** + * Sets the size of the chunk. + * + * To ensure data integrity, as well as to allow for lower memory + * consumption, data is sent/received in chunks. This function + * allows you to set the size of each chunk. The default is 0xFFFFF. + * + * @param int $size The desired size of the chunk, in bytes. + * @param string $direction The chunk of which direction to set. Valid + * values are the DIRECTION_* constants. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function setChunk($size, $direction = self::DIRECTION_ALL) + { + $size = (int) $size; + if ($size <= 0) { + return false; + } + switch($direction) { + case self::DIRECTION_SEND: + case self::DIRECTION_RECEIVE: + $this->chunkSize[$direction] = $size; + return true; + case self::DIRECTION_ALL: + $this->chunkSize[self::DIRECTION_SEND] + = $this->chunkSize[self::DIRECTION_RECEIVE] = $size; + return true; + } + return false; + } + + /** + * Gets the size of the chunk. + * + * @param string $direction The chunk of which direction to get. Valid + * values are the DIRECTION_* constants. + * + * @return int|array|false The chunk size in bytes, + * or an array of chunk sizes with the directions as keys. + * FALSE on invalid direction. + */ + public function getChunk($direction = self::DIRECTION_ALL) + { + switch($direction) { + case self::DIRECTION_SEND: + case self::DIRECTION_RECEIVE: + return $this->chunkSize[$direction]; + case self::DIRECTION_ALL: + return $this->chunkSize; + } + return false; + } + + /** + * Sends a string or stream over the wrapped stream. + * + * Sends a string or stream over the wrapped stream. If a seekable stream is + * provided, it will be seeked back to the same position it was passed as, + * regardless of the $offset parameter. + * + * @param string|resource $contents The string or stream to send. + * @param int $offset The offset from which to start sending. + * If a stream is provided, and this is set to NULL, sending will start + * from the current stream position. + * @param int $length The maximum length to send. If omitted, + * the string/stream will be sent to its end. + * + * @return int The number of bytes sent. + */ + public function send($contents, $offset = null, $length = null) + { + $bytes = 0; + $chunkSize = $this->chunkSize[self::DIRECTION_SEND]; + $lengthIsNotNull = null !== $length; + $offsetIsNotNull = null !== $offset; + if (self::isStream($contents)) { + if ($offsetIsNotNull) { + $oldPos = ftell($contents); + fseek($contents, $offset, SEEK_SET); + } + while (!feof($contents)) { + if ($lengthIsNotNull + && 0 === $chunkSize = min($chunkSize, $length - $bytes) + ) { + break; + } + $bytesNow = @fwrite( + $this->stream, + fread($contents, $chunkSize) + ); + if (0 != $bytesNow) { + $bytes += $bytesNow; + } elseif ($this->isBlocking || false === $bytesNow) { + throw $this->createException( + 'Failed while sending stream.', + 2, + null, + $bytes + ); + } else { + usleep(300000); + } + $this->isAcceptingData(null); + } + if ($offsetIsNotNull) { + fseek($contents, $oldPos, SEEK_SET); + } else { + fseek($contents, -$bytes, SEEK_CUR); + } + } else { + $contents = (string) $contents; + if ($offsetIsNotNull) { + $contents = substr($contents, $offset); + } + if ($lengthIsNotNull) { + $contents = substr($contents, 0, $length); + } + $bytesToSend = (double) sprintf('%u', strlen($contents)); + while ($bytes < $bytesToSend) { + $bytesNow = @fwrite( + $this->stream, + substr($contents, $bytes, $chunkSize) + ); + if (0 != $bytesNow) { + $bytes += $bytesNow; + } elseif ($this->isBlocking || false === $bytesNow) { + throw $this->createException( + 'Failed while sending string.', + 3, + null, + $bytes + ); + } else { + usleep(300000); + } + $this->isAcceptingData(null); + } + } + return $bytes; + } + + /** + * Reads from the wrapped stream to receive. + * + * Reads from the wrapped stream to receive content as a string. + * + * @param int $length The number of bytes to receive. + * @param string $what Descriptive string about what is being received + * (used in exception messages). + * + * @return string The received content. + */ + public function receive($length, $what = 'data') + { + $result = ''; + $chunkSize = $this->chunkSize[self::DIRECTION_RECEIVE]; + while ($length > 0) { + while ($this->isAvailable()) { + $fragment = fread($this->stream, min($length, $chunkSize)); + if ('' != $fragment) { + $length -= strlen($fragment); + $result .= $fragment; + continue 2; + } elseif (!$this->isBlocking && !(false === $fragment)) { + usleep(3000); + continue 2; + } + } + throw $this->createException( + "Failed while receiving {$what}", + 4, + null, + $result + ); + } + return $result; + } + + /** + * Reads from the wrapped stream to receive. + * + * Reads from the wrapped stream to receive content as a stream. + * + * @param int $length The number of bytes to receive. + * @param FilterCollection $filters A collection of filters to apply to the + * stream while receiving. Note that the filters will not be present on + * the stream after receiving is done. + * @param string $what Descriptive string about what is being + * received (used in exception messages). + * + * @return resource The received content. + */ + public function receiveStream( + $length, + FilterCollection $filters = null, + $what = 'stream data' + ) { + $result = fopen('php://temp', 'r+b'); + $appliedFilters = array(); + if (null !== $filters) { + foreach ($filters as $filtername => $params) { + $appliedFilters[] = stream_filter_append( + $result, + $filtername, + STREAM_FILTER_WRITE, + $params + ); + } + } + + $chunkSize = $this->chunkSize[self::DIRECTION_RECEIVE]; + while ($length > 0) { + while ($this->isAvailable()) { + $fragment = fread($this->stream, min($length, $chunkSize)); + if ('' != $fragment) { + $length -= strlen($fragment); + fwrite($result, $fragment); + continue 2; + } elseif (!$this->isBlocking && !(false === $fragment)) { + usleep(3000); + continue 2; + } + } + + foreach ($appliedFilters as $filter) { + stream_filter_remove($filter); + } + rewind($result); + throw $this->createException( + "Failed while receiving {$what}", + 5, + null, + $result + ); + } + + foreach ($appliedFilters as $filter) { + stream_filter_remove($filter); + } + rewind($result); + return $result; + } + + /** + * Checks whether the stream is available for operations. + * + * For network streams, this means whether the other end has closed the + * connection. + * + * @return bool TRUE if the stream is available, FALSE otherwise. + */ + public function isAvailable() + { + return self::isStream($this->stream) && !feof($this->stream); + } + + /** + * Checks whether there is data to be read from the wrapped stream. + * + * @param int|null $sTimeout If theere isn't data awaiting currently, + * wait for it this many seconds for data to arrive. If NULL is + * specified, wait indefinetly for that. + * @param int $usTimeout Microseconds to add to the waiting time. + * + * @return bool TRUE if there is data to be read, FALSE otherwise. + * @SuppressWarnings(PHPMD.ShortVariable) + */ + public function isDataAwaiting($sTimeout = 0, $usTimeout = 0) + { + if (self::isStream($this->stream)) { + if (null === $sTimeout && !$this->isBlocking) { + $meta = stream_get_meta_data($this->stream); + return !$meta['eof']; + } + + $w = $e = null; + $r = array($this->stream); + return 1 === @/* due to PHP bug #54563 */stream_select( + $r, + $w, + $e, + $sTimeout, + $usTimeout + ); + } + return false; + } + + /** + * Checks whether the wrapped stream can be written to without a block. + * + * @param int|null $sTimeout If the stream isn't currently accepting data, + * wait for it this many seconds to start accepting data. If NULL is + * specified, wait indefinetly for that. + * @param int $usTimeout Microseconds to add to the waiting time. + * + * @return bool TRUE if the wrapped stream would not block on a write, FALSE + * otherwise. + * @SuppressWarnings(PHPMD.ShortVariable) + */ + public function isAcceptingData($sTimeout = 0, $usTimeout = 0) + { + if (self::isStream($this->stream)) { + if (!$this->isBlocking) { + $meta = stream_get_meta_data($this->stream); + return !$meta['eof']; + } elseif (feof($this->stream)) { + return false; + } + + $r = $e = null; + $w = array($this->stream); + return 1 === @/* due to PHP bug #54563 */stream_select( + $r, + $w, + $e, + $sTimeout, + $usTimeout + ); + } + return false; + } + + /** + * Closes the opened stream, unless it's a persistent one. + */ + public function __destruct() + { + if ((!$this->persist) && $this->autoClose) { + $this->close(); + } + } + + /** + * Closes the opened stream, even if it is a persistent one. + * + * @return bool TRUE on success, FALSE on failure. + */ + public function close() + { + return self::isStream($this->stream) && fclose($this->stream); + } + + /** + * Creates a new exception. + * + * Creates a new exception. Used by the rest of the functions in this class. + * Override in derived classes for custom exception handling. + * + * @param string $message The exception message. + * @param int $code The exception code. + * @param E|null $previous Previous exception thrown, + * or NULL if there is none. + * @param int|string|resource|null $fragment The fragment up until the + * point of failure. + * On failure with sending, this is the number of bytes sent + * successfully before the failure. + * On failure when receiving, this is a string/stream holding + * the contents received successfully before the failure. + * + * @return StreamException The exception to then be thrown. + */ + protected function createException( + $message, + $code = 0, + E $previous = null, + $fragment = null + ) { + return new StreamException($message, $code, $previous, $fragment); + } +} diff --git a/system/autoload/PEAR2/Net/Transmitter/StreamException.php b/system/autoload/PEAR2/Net/Transmitter/StreamException.php new file mode 100644 index 0000000..7a00f1c --- /dev/null +++ b/system/autoload/PEAR2/Net/Transmitter/StreamException.php @@ -0,0 +1,119 @@ + + * @copyright 2011 Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 1.0.0a5 + * @link http://pear2.php.net/PEAR2_Net_Transmitter + */ +/** + * The namespace declaration. + */ +namespace PEAR2\Net\Transmitter; + +/** + * Base for this exception. + */ +use RuntimeException; + +/** + * Used to enable any exception in chaining. + */ +use Exception as E; + +/** + * Exception thrown when something goes wrong with the connection. + * + * @category Net + * @package PEAR2_Net_Transmitter + * @author Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Net_Transmitter + */ +class StreamException extends RuntimeException implements Exception +{ + /** + * @var int|string|resource|null The fragment up until the point of failure. + * On failure with sending, this is the number of bytes sent + * successfully before the failure. + * On failure when receiving, this is a string/stream holding + * the contents received successfully before the failure. + * NULL if the failure occured before the operation started. + */ + protected $fragment = null; + + /** + * Creates a new stream exception. + * + * @param string $message The Exception message to throw. + * @param int $code The Exception code. + * @param E|null $previous Previous exception thrown, + * or NULL if there is none. + * @param int|string|resource|null $fragment The fragment up until the + * point of failure. + * On failure with sending, this is the number of bytes sent + * successfully before the failure. + * On failure when receiving, this is a string/stream holding + * the contents received successfully before the failure. + * NULL if the failure occured before the operation started. + */ + public function __construct( + $message, + $code, + E $previous = null, + $fragment = null + ) { + parent::__construct($message, $code, $previous); + $this->fragment = $fragment; + } + + /** + * Gets the stream fragment. + * + * @return int|string|resource|null The fragment up until the + * point of failure. + * On failure with sending, this is the number of bytes sent + * successfully before the failure. + * On failure when receiving, this is a string/stream holding + * the contents received successfully before the failure. + * NULL if the failure occured before the operation started. + */ + public function getFragment() + { + return $this->fragment; + } + + // @codeCoverageIgnoreStart + // Unreliable in testing. + + /** + * Returns a string representation of the exception. + * + * @return string The exception as a string. + */ + public function __toString() + { + $result = parent::__toString(); + if (null !== $this->fragment) { + $result .= "\nFragment: "; + if (is_scalar($this->fragment)) { + $result .= (string)$this->fragment; + } else { + $result .= stream_get_contents($this->fragment); + } + } + return $result; + } + // @codeCoverageIgnoreEnd +} diff --git a/system/autoload/PEAR2/Net/Transmitter/TcpClient.php b/system/autoload/PEAR2/Net/Transmitter/TcpClient.php new file mode 100644 index 0000000..c2f0ab7 --- /dev/null +++ b/system/autoload/PEAR2/Net/Transmitter/TcpClient.php @@ -0,0 +1,400 @@ + + * @copyright 2011 Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 1.0.0a5 + * @link http://pear2.php.net/PEAR2_Net_Transmitter + */ +/** + * The namespace declaration. + */ +namespace PEAR2\Net\Transmitter; + +/** + * Used for managing persistent connections. + */ +use PEAR2\Cache\SHM; + +/** + * Used for matching arbitrary exceptions in + * {@link TcpClient::createException()} and releasing locks properly. + */ +use Exception as E; + +/** + * A socket transmitter. + * + * This is a convinience wrapper for socket functionality. Used to ensure data + * integrity. + * + * @category Net + * @package PEAR2_Net_Transmitter + * @author Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Net_Transmitter + */ +class TcpClient extends NetworkStream +{ + + /** + * @var int The error code of the last error on the socket. + */ + protected $errorNo = 0; + + /** + * @var string The error message of the last error on the socket. + */ + protected $errorStr = null; + + /** + * @var SHM Persistent connection handler. Remains NULL for non-persistent + * connections. + */ + protected $shmHandler = null; + + /** + * @var array An array with all connections from this PHP request (as keys) + * and their lock state (as a value). + */ + protected static $lockState = array(); + + protected static $cryptoScheme = array( + parent::CRYPTO_OFF => 'tcp', + parent::CRYPTO_SSL2 => 'sslv2', + parent::CRYPTO_SSL3 => 'sslv3', + parent::CRYPTO_SSL => 'ssl', + parent::CRYPTO_TLS => 'tls' + ); + + /** + * @var string The URI of this connection. + */ + protected $uri; + + /** + * Creates a new connection with the specified options. + * + * @param string $host Hostname (IP or domain) of the server. + * @param int $port The port on the server. + * @param bool $persist Whether or not the connection should be a + * persistent one. + * @param float $timeout The timeout for the connection. + * @param string $key A string that uniquely identifies the + * connection. + * @param string $crypto Encryption setting. Must be one of the + * NetworkStream::CRYPTO_* constants. By default, encryption is + * disabled. If the setting has an associated scheme for it, it will be + * used, and if not, the setting will be adjusted right after the + * connection is estabilished. + * @param resource $context A context for the socket. + */ + public function __construct( + $host, + $port, + $persist = false, + $timeout = null, + $key = '', + $crypto = parent::CRYPTO_OFF, + $context = null + ) { + $this->streamType = '_CLIENT'; + + if (strpos($host, ':') !== false) { + $host = "[{$host}]"; + } + $flags = STREAM_CLIENT_CONNECT; + if ($persist) { + $flags |= STREAM_CLIENT_PERSISTENT; + } + + $timeout + = null == $timeout ? ini_get('default_socket_timeout') : $timeout; + + $key = rawurlencode($key); + + if (null === $context) { + $context = stream_context_get_default(); + } elseif ((!is_resource($context)) + || ('stream-context' !== get_resource_type($context)) + ) { + throw $this->createException('Invalid context supplied.', 6); + } + $hasCryptoScheme = array_key_exists($crypto, static::$cryptoScheme); + $scheme = $hasCryptoScheme ? static::$cryptoScheme[$crypto] : 'tcp'; + $this->uri = "{$scheme}://{$host}:{$port}/{$key}"; + set_error_handler(array($this, 'handleError')); + try { + parent::__construct( + stream_socket_client( + $this->uri, + $this->errorNo, + $this->errorStr, + $timeout, + $flags, + $context + ) + ); + restore_error_handler(); + } catch (E $e) { + restore_error_handler(); + if (0 === $this->errorNo) { + throw $this->createException( + 'Failed to initialize socket.', + 7, + $e + ); + } + throw $this->createException( + 'Failed to connect with socket.', + 8, + $e + ); + } + + if ($hasCryptoScheme) { + $this->crypto = $crypto; + } elseif (parent::CRYPTO_OFF !== $crypto) { + $this->setCrypto($crypto); + } + $this->setIsBlocking(parent::CRYPTO_OFF === $crypto); + + if ($persist) { + $this->shmHandler = SHM::factory( + __CLASS__ . ' ' . $this->uri . ' ' + ); + self::$lockState[$this->uri] = self::DIRECTION_NONE; + } + } + + /** + * Creates a new exception. + * + * Creates a new exception. Used by the rest of the functions in this class. + * + * @param string $message The exception message. + * @param int $code The exception code. + * @param E|null $previous Previous exception thrown, + * or NULL if there is none. + * @param int|string|resource|null $fragment The fragment up until the + * point of failure. + * On failure with sending, this is the number of bytes sent + * successfully before the failure. + * On failure when receiving, this is a string/stream holding + * the contents received successfully before the failure. + * + * @return SocketException The exception to then be thrown. + */ + protected function createException( + $message, + $code = 0, + E $previous = null, + $fragment = null + ) { + return new SocketException( + $message, + $code, + $previous, + $fragment, + $this->errorNo, + $this->errorStr + ); + } + + /** + * Locks transmission. + * + * Locks transmission in one or more directions. Useful when dealing with + * persistent connections. Note that every send/receive call implicitly + * calls this function and then restores it to the previous state. You only + * need to call this function if you need to do an uninterrputed sequence of + * such calls. + * + * @param int $direction The direction(s) to have locked. Acceptable values + * are the DIRECTION_* constants. If a lock for a direction can't be + * obtained immediatly, the function will block until one is aquired. + * Note that if you specify {@link DIRECTION_ALL}, the sending lock will + * be obtained before the receiving one, and if obtaining the receiving + * lock afterwards fails, the sending lock will be released too. + * @param bool $replace Whether to replace all locks with the specified + * ones. Setting this to FALSE will make the function only obtain the + * locks which are not already obtained. + * + * @return int|false The previous state or FALSE if the connection is not + * persistent or arguments are invalid. + */ + public function lock($direction = self::DIRECTION_ALL, $replace = false) + { + if ($this->persist && is_int($direction)) { + $old = self::$lockState[$this->uri]; + + if ($direction & self::DIRECTION_SEND) { + if (($old & self::DIRECTION_SEND) + || $this->shmHandler->lock(self::DIRECTION_SEND) + ) { + self::$lockState[$this->uri] |= self::DIRECTION_SEND; + } else { + throw new LockException('Unable to obtain sending lock.'); + } + } elseif ($replace) { + if (!($old & self::DIRECTION_SEND) + || $this->shmHandler->unlock(self::DIRECTION_SEND) + ) { + self::$lockState[$this->uri] &= ~self::DIRECTION_SEND; + } else { + throw new LockException('Unable to release sending lock.'); + } + } + + try { + if ($direction & self::DIRECTION_RECEIVE) { + if (($old & self::DIRECTION_RECEIVE) + || $this->shmHandler->lock(self::DIRECTION_RECEIVE) + ) { + self::$lockState[$this->uri] |= self::DIRECTION_RECEIVE; + } else { + throw new LockException( + 'Unable to obtain receiving lock.' + ); + } + } elseif ($replace) { + if (!($old & self::DIRECTION_RECEIVE) + || $this->shmHandler->unlock(self::DIRECTION_RECEIVE) + ) { + self::$lockState[$this->uri] + &= ~self::DIRECTION_RECEIVE; + } else { + throw new LockException( + 'Unable to release receiving lock.' + ); + } + } + } catch (LockException $e) { + if ($direction & self::DIRECTION_SEND + && !($old & self::DIRECTION_SEND) + ) { + $this->shmHandler->unlock(self::DIRECTION_SEND); + } + throw $e; + } + return $old; + } + return false; + } + + + /** + * Sends a string or stream to the server. + * + * Sends a string or stream to the server. If a seekable stream is + * provided, it will be seeked back to the same position it was passed as, + * regardless of the $offset parameter. + * + * @param string|resource $contents The string or stream to send. + * @param int $offset The offset from which to start sending. + * If a stream is provided, and this is set to NULL, sending will start + * from the current stream position. + * @param int $length The maximum length to send. If omitted, + * the string/stream will be sent to its end. + * + * @return int The number of bytes sent. + */ + public function send($contents, $offset = null, $length = null) + { + if (false === ($previousState = $this->lock(self::DIRECTION_SEND)) + && $this->persist + ) { + throw $this->createException( + 'Unable to obtain sending lock', + 10 + ); + } + try { + $result = parent::send($contents, $offset, $length); + } catch (E $e) { + $this->lock($previousState, true); + throw $e; + } + $this->lock($previousState, true); + return $result; + } + + /** + * Receives data from the server. + * + * Receives data from the server as a string. + * + * @param int $length The number of bytes to receive. + * @param string $what Descriptive string about what is being received + * (used in exception messages). + * + * @return string The received content. + */ + public function receive($length, $what = 'data') + { + if (false === ($previousState = $this->lock(self::DIRECTION_RECEIVE)) + && $this->persist + ) { + throw $this->createException( + 'Unable to obtain receiving lock', + 9 + ); + } + try { + $result = parent::receive($length, $what); + } catch (E $e) { + $this->lock($previousState, true); + throw $e; + } + $this->lock($previousState, true); + return $result; + } + + /** + * Receives data from the server. + * + * Receives data from the server as a stream. + * + * @param int $length The number of bytes to receive. + * @param FilterCollection $filters A collection of filters to apply to the + * stream while receiving. Note that the filters will not be present on + * the stream after receiving is done. + * @param string $what Descriptive string about what is being + * received (used in exception messages). + * + * @return resource The received content. + */ + public function receiveStream( + $length, + FilterCollection $filters = null, + $what = 'stream data' + ) { + if (false === ($previousState = $this->lock(self::DIRECTION_RECEIVE)) + && $this->persist + ) { + throw $this->createException( + 'Unable to obtain receiving lock', + 9 + ); + } + try { + $result = parent::receiveStream($length, $filters, $what); + } catch (E $e) { + $this->lock($previousState, true); + throw $e; + } + $this->lock($previousState, true); + return $result; + } +} diff --git a/system/autoload/PEAR2/Net/Transmitter/TcpServerConnection.php b/system/autoload/PEAR2/Net/Transmitter/TcpServerConnection.php new file mode 100644 index 0000000..97a14ab --- /dev/null +++ b/system/autoload/PEAR2/Net/Transmitter/TcpServerConnection.php @@ -0,0 +1,147 @@ + + * @copyright 2011 Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @version 1.0.0a5 + * @link http://pear2.php.net/PEAR2_Net_Transmitter + */ +/** + * The namespace declaration. + */ +namespace PEAR2\Net\Transmitter; + +use Exception as E; + +/** + * A transmitter for connections to a socket server. + * + * This is a convinience wrapper for functionality of socket server connections. + * Used to ensure data integrity. Server handling is not part of the class in + * order to allow its usage as part of various server implementations (e.g. fork + * and/or sequential). + * + * @category Net + * @package PEAR2_Net_Transmitter + * @author Vasil Rangelov + * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 + * @link http://pear2.php.net/PEAR2_Net_Transmitter + */ +class TcpServerConnection extends NetworkStream +{ + + /** + * @var string The IP address of the connected client. + */ + protected $peerIP; + + /** + * @var int The port of the connected client. + */ + protected $peerPort; + + /** + * Creates a new connection with the specified options. + * + * @param resource $server A socket server, created with + * {@link stream_socket_server()}. + * @param float $timeout The timeout for the connection. + */ + public function __construct($server, $timeout = null) + { + $this->streamType = '_SERVER'; + + if (!self::isStream($server)) { + throw $this->createException('Invalid server supplied.', 9); + } + $timeout + = null == $timeout ? ini_get('default_socket_timeout') : $timeout; + + set_error_handler(array($this, 'handleError')); + try { + parent::__construct( + stream_socket_accept($server, $timeout, $peername) + ); + restore_error_handler(); + $portString = strrchr($peername, ':'); + $this->peerPort = (int) substr($portString, 1); + $ipString = substr( + $peername, + 0, + strlen($peername) - strlen($portString) + ); + if (strpos($ipString, '[') === 0 + && strpos(strrev($ipString), ']') === 0 + ) { + $ipString = substr($ipString, 1, strlen($ipString) - 2); + } + $this->peerIP = $ipString; + } catch (E $e) { + restore_error_handler(); + throw $this->createException( + 'Failed to initialize connection.', + 10, + $e + ); + } + } + + /** + * Gets the IP address of the connected client. + * + * @return string The IP address of the connected client. + */ + public function getPeerIP() + { + return $this->peerIP; + } + + /** + * Gets the port of the connected client. + * + * @return int The port of the connected client. + */ + public function getPeerPort() + { + return $this->peerPort; + } + + /** + * Creates a new exception. + * + * Creates a new exception. Used by the rest of the functions in this class. + * + * @param string $message The exception message. + * @param int $code The exception code. + * @param E|null $previous Previous exception thrown, or NULL if there + * is none. + * @param string|null $fragment The fragment up until the point of failure. + * NULL if the failure occured before the operation started. + * + * @return SocketException The exception to then be thrown. + */ + protected function createException( + $message, + $code = 0, + E $previous = null, + $fragment = null + ) { + return new SocketException( + $message, + $code, + $previous, + $fragment + ); + } +} diff --git a/system/autoload/Paginator.php b/system/autoload/Paginator.php new file mode 100644 index 0000000..569288c --- /dev/null +++ b/system/autoload/Paginator.php @@ -0,0 +1,105 @@ + +* @version 5.0 +* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing +* @license GNU General Public License version 2 or later; see LICENSE.txt +* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4 +**/ + +Class Paginator +{ + public static function bootstrap($table, $w1='',$c1='', $w2='', $c2= '', $w3='',$c3='', $w4='', $c4= '', $per_page = '10') + { + global $routes; + global $_L; + $url = U.$routes['0'].'/'.$routes['1'].'/'; + $adjacents = "2"; + $page = (int)(!isset($routes['2']) ? 1 : $routes['2']); + $pagination = ""; + + if($w1 != ''){ + $totalReq = ORM::for_table($table)->where($w1,$c1)->count(); + }elseif($w2 != ''){ + $totalReq = ORM::for_table($table)->where($w1,$c1)->where($w2,$c2)->count(); + }elseif($w3 != ''){ + $totalReq = ORM::for_table($table)->where($w1,$c1)->where($w2,$c2)->where($w3,$c3)->count(); + }elseif($w4 != ''){ + $totalReq = ORM::for_table($table)->where($w1,$c1)->where($w2,$c2)->where($w3,$c3)->where($w4,$c4)->count(); + }else{ + $totalReq = ORM::for_table($table)->count(); + } + + $i = 0; + $page = ($page == 0 ? 1 : $page); + $start = ($page - 1) * $per_page; + + $prev = $page - 1; + $next = $page + 1; + $lastpage = ceil($totalReq / $per_page); + + $lpm1 = $lastpage - 1; + $limit = $per_page; + $startpoint = ($page * $limit) - $limit; + + if ($lastpage >= 1) { + $pagination .= '
    '; + if ($lastpage < 7 + ($adjacents * 2)) { + for ($counter = 1; $counter <= $lastpage; $counter++) { + if ($counter == $page) + $pagination .= "
  • $counter
  • "; + else + $pagination .= "
  • $counter
  • "; + } + } elseif ($lastpage > 5 + ($adjacents * 2)) { + if ($page < 1 + ($adjacents * 2)) { + for ($counter = 1; $counter < 4 + ($adjacents * 2); $counter++) { + if ($counter == $page) + $pagination .= "
  • $counter
  • "; + else + $pagination .= "
  • $counter
  • "; + } + $pagination .= "
  • ...
  • "; + $pagination .= "
  • $lpm1
  • "; + $pagination .= "
  • $lastpage
  • "; + } elseif ($lastpage - ($adjacents * 2) > $page && $page > ($adjacents * 2)) { + $pagination .= "
  • 1
  • "; + $pagination .= "
  • 2
  • "; + $pagination .= "
  • ...
  • "; + for ($counter = $page - $adjacents; $counter <= $page + $adjacents; $counter++) { + if ($counter == $page) + $pagination .= "
  • $counter
  • "; + else + $pagination .= "
  • $counter
  • "; + } + $pagination .= "
  • ...
  • "; + $pagination .= "
  • $lpm1
  • "; + $pagination .= "
  • $lastpage
  • "; + } else { + $pagination .= "
  • 1
  • "; + $pagination .= "
  • 2
  • "; + $pagination .= "
  • ...
  • "; + for ($counter = $lastpage - (2 + ($adjacents * 2)); $counter <= $lastpage; $counter++) { + if ($counter == $page) + $pagination .= "
  • $counter
  • "; + else + $pagination .= "
  • $counter
  • "; + } + } + } + + if ($page < $counter - 1) { + $pagination .= "
  • ".$_L['Next']."
  • "; + $pagination .= "
  • ".$_L['Last']."
  • "; + } else { + $pagination .= "
  • ".$_L['Next']."
  • "; + $pagination .= "
  • ".$_L['Last']."
  • "; + } + $pagination .= "
"; + + $gen = array("startpoint" => $startpoint, "limit" => $limit, "found" => $totalReq, "page" => $page, "lastpage" => $lastpage, "contents" => $pagination); + return $gen; + } + } +} \ No newline at end of file diff --git a/system/autoload/Password.php b/system/autoload/Password.php new file mode 100644 index 0000000..0775281 --- /dev/null +++ b/system/autoload/Password.php @@ -0,0 +1,34 @@ + +* @version 5.0 +* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing +* @license GNU General Public License version 2 or later; see LICENSE.txt +* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4 +**/ + +Class Password{ + + public static function _crypt($password) { + return crypt($password); + } + + public static function _verify($user_input, $hashed_password){ + if (crypt($user_input, $hashed_password) == $hashed_password) { + return true; + } + return false; + } + public static function _uverify($user_input, $hashed_password){ + if ($user_input == $hashed_password) { + return true; + } + return false; + } + public static function _gen(){ + $pass = substr(str_shuffle(str_repeat('ABCDEFGHIJKLMNPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@#!123456789', 8)), 0, 8); + return $pass; + } + +} \ No newline at end of file diff --git a/system/autoload/Router.php b/system/autoload/Router.php new file mode 100644 index 0000000..09d9891 --- /dev/null +++ b/system/autoload/Router.php @@ -0,0 +1,16 @@ + +* @version 5.0 +* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing +* @license GNU General Public License version 2 or later; see LICENSE.txt +* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4 +**/ + +Class Router{ + public static function _info($name){ + $d = ORM::for_table('tbl_routers')->where('name',$name)->find_one(); + return $d; + } +} \ No newline at end of file diff --git a/system/autoload/Timezone.php b/system/autoload/Timezone.php new file mode 100644 index 0000000..b914d2f --- /dev/null +++ b/system/autoload/Timezone.php @@ -0,0 +1,44 @@ + +* @version 5.0 +* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing +* @license GNU General Public License version 2 or later; see LICENSE.txt +* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4 +**/ + +class Timezone { + public static function timezoneList() + { + $timezoneIdentifiers = DateTimeZone::listIdentifiers(); + $utcTime = new DateTime('now', new DateTimeZone('UTC')); + + $tempTimezones = array(); + foreach ($timezoneIdentifiers as $timezoneIdentifier) { + $currentTimezone = new DateTimeZone($timezoneIdentifier); + + $tempTimezones[] = array( + 'offset' => (int)$currentTimezone->getOffset($utcTime), + 'identifier' => $timezoneIdentifier + ); + } + + // Sort the array by offset,identifier ascending + usort($tempTimezones, function($a, $b) { + return ($a['offset'] == $b['offset']) + ? strcmp($a['identifier'], $b['identifier']) + : $a['offset'] - $b['offset']; + }); + + $timezoneList = array(); + foreach ($tempTimezones as $tz) { + $sign = ($tz['offset'] > 0) ? '+' : '-'; + $offset = gmdate('H:i', abs($tz['offset'])); + $timezoneList[$tz['identifier']] = '(UTC ' . $sign . $offset . ') ' . + $tz['identifier']; + } + + return $timezoneList; + } +} \ No newline at end of file diff --git a/system/autoload/User.php b/system/autoload/User.php new file mode 100644 index 0000000..2abdc37 --- /dev/null +++ b/system/autoload/User.php @@ -0,0 +1,22 @@ + +* @version 5.0 +* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing +* @license GNU General Public License version 2 or later; see LICENSE.txt +* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4 +**/ + +Class User{ + public static function _info(){ + $id = $_SESSION['uid']; + $d = ORM::for_table('tbl_customers')->find_one($id); + return $d; + } + public static function _billing(){ + $id = $_SESSION['uid']; + $d = ORM::for_table('tbl_user_recharges')->where('customer_id',$id)->find_one(); + return $d; + } +} \ No newline at end of file diff --git a/system/autoload/Validator.php b/system/autoload/Validator.php new file mode 100644 index 0000000..d58f439 --- /dev/null +++ b/system/autoload/Validator.php @@ -0,0 +1,284 @@ + +* @version 5.0 +* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing +* @license GNU General Public License version 2 or later; see LICENSE.txt +* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4 +**/ + +/** + * Validator class + */ +class Validator{ + + /** + * String text finder + * + * @access private + * @param string $string + * @param array $hits + * @return void + */ + private static function textHit($string, $exclude=""){ + if(empty($exclude)) return false; + if(is_array($exclude)){ + foreach($exclude as $text){ + if(strstr($string, $text)) return true; + } + }else{ + if(strstr($string, $exclude)) return true; + } + return false; + } + + /** + * Number compare + * + * @access private + * @param int $integer + * @param int $max + * @param int $min + * @return bool + */ + private static function numberBetween($integer, $max=null, $min=0){ + if(is_numeric($min) && $integer <= $min) return false; + if(is_numeric($max) && $integer >= $max) return false; + return true; + } + + /** + * Email addres check + * + * @access public + * @param string $string + * @param array $exclude + * @return bool + */ + public static function Email($string, $exclude=""){ + if(self::textHit($string, $exclude)) return false; + return (bool)preg_match("/^([a-z0-9])(([-a-z0-9._])*([a-z0-9]))*\@([a-z0-9])(([a-z0-9-])*([a-z0-9]))+(\.([a-z0-9])([-a-z0-9_-])?([a-z0-9])+)+$/i", $string); + } + + /** + * URL check + * + * @access public + * @param strin $string + * @return bool + */ + public static function Url($string, $exclude=""){ + if(self::textHit($string, $exclude)) return false; + return (bool)preg_match("/^(http|https|ftp):\/\/([A-Z0-9][A-Z0-9_-]*(?:\.[A-Z0-9][A-Z0-9_-]*)+):?(\d+)?\/?/i", $string); + } + + /** + * IP + * + * @access public + * @param string $string + * @return void + */ + public static function Ip($string){ + return (bool)preg_match("/^(1?\d{1,2}|2([0-4]\d|5[0-5]))(\.(1?\d{1,2}|2([0-4]\d|5[0-5]))){3}$/", $string); + } + + /** + * Check if it is an number + * + * @access public + * @param int $integer + * @param int $max + * @param int $min + * @return bool + */ + public static function Number($integer, $max=null, $min=0){ + if(preg_match("/^\-?\+?[0-9e1-9]+$/",$integer)){ + if(!self::numberBetween($integer, $max, $min)) return false; + return true; + } + return false; + } + + /** + * Check if it is an unsigned number + * + * @access public + * @param int $integer + * @return bool + */ + public static function UnsignedNumber($integer){ + return (bool)preg_match("/^\+?[0-9]+$/",$integer); + } + + /** + * Float + * + * @access public + * @param string $string + * @return bool + */ + public static function Float($string){ + return (bool)($string==strval(floatval($string)))? true : false; + } + + /** + * Alpha check + * + * @access public + * @param string $string + * @return void + */ + public static function Alpha($string){ + return (bool)preg_match("/^[a-zA-Z]+$/", $string); + } + + /** + * Alpha numeric check + * + * @access public + * @param string $string + * @return void + */ + public static function AlphaNumeric($string){ + return (bool)preg_match("/^[0-9a-zA-Z]+$/", $string); + } + + /** + * Specific chars check + * + * @access public + * @param string $string + * @param array $allowed + * @return void + */ + public static function Chars($string, $allowed=array("a-z")){ + return (bool)preg_match("/^[" . implode("", $allowed) . "]+$/", $string); + } + + /** + * Check length of an string + * + * @access public + * @param string $stirng + * @param int $max + * @param int $min + * @return bool + */ + public static function Length($string, $max=null, $min=0){ + $length = strlen($string); + if(!self::numberBetween($length, $max, $min)) return false; + return true; + } + + /** + * Hex color check + * + * @access public + * @param string $string + * @return void + */ + public static function HexColor($string){ + return (bool)preg_match("/^(#)?([0-9a-f]{1,2}){3}$/i", $string); + } + + /** + * Data validation + * + * Does'nt matter how you provide the date + * dd/mm/yyyy + * dd-mm-yyyy + * yyyy/mm/dd + * yyyy-mm-dd + * + * @access public + * @param string $string + * @return bool + */ + public static function Date($string){ + $date = date('Y', strtotime($string)); + return ($date == "1970" || $date == '') ? false : true; + } + + /** + * Older than check + * + * @access public + * @param string $string + * @param int $age + * @return bool + */ + public static function OlderThan($string, $age){ + $date = date('Y', strtotime($string)); + if($date == "1970" || $date == '') return false; + return (date('Y') - $date) > $age ? true : false; + } + + /** + * XML valid + * + * @access public + * @param string $string + * @return bool + */ + public static function Xml($string){ + $Xml = @simplexml_load_string($string); + return ($Xml === false) ? false : true; + } + + /** + * Is filesize between + * + * @access public + * @param string $file + * @param int $max + * @param int $min + * @return bool + */ + public static function FilesizeBetween($file, $max=null, $min=0){ + $filesize = filesize($file); + return self::numberBetween($filesize, $max, $min); + } + + /** + * Is image width between + * + * @access public + * @param string $image + * @param int $max_width + * @param int $min_width + * @param int $max_height + * @param int $min_height + * @return void + */ + public static function ImageSizeBetween($image, $max_width="", $min_width=0, $max_height="", $min_height=0){ + $size = getimagesize($image); + if(!self::numberBetween($size[0], $max_width, $min_width)) return false; + if(!self::numberBetween($size[1], $max_height, $min_height)) return false; + return true; + } + + /** + * Phone numbers + * + * @access public + * @param string $phone + * @return bool + */ + public static function Phone($phone){ + $formats = array( '###-###-####', + '####-###-###', + '(###) ###-###', + '####-####-####', + '##-###-####-####', + '####-####', + '###-###-###', + '#####-###-###', + '##########', + '####-##-##-##'); + $format = trim(preg_replace("/[0-9]/", "#", $phone)); + return (bool)in_array($format, $formats); + } + +} \ No newline at end of file diff --git a/system/autoload/index.html b/system/autoload/index.html new file mode 100644 index 0000000..9757970 --- /dev/null +++ b/system/autoload/index.html @@ -0,0 +1,8 @@ + + + 403 Forbidden + + +

Directory access is forbidden.

+ + \ No newline at end of file diff --git a/system/boot.php b/system/boot.php new file mode 100644 index 0000000..531b90f --- /dev/null +++ b/system/boot.php @@ -0,0 +1,196 @@ + +* @version 5.0 +* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing +* @license GNU General Public License version 2 or later; see LICENSE.txt +* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4 +**/ +session_start(); +function r2($to,$ntype='e',$msg=''){ + if($msg==''){ + header("location: $to"); + exit; + } + $_SESSION['ntype']=$ntype; + $_SESSION['notify']=$msg; + header("location: $to"); + exit; +} + +if (file_exists('system/config.php')) { + require('system/config.php'); +} else { + r2('system/install'); +} + +function safedata($value){ + $value = trim($value); + return $value; +} + +function _post($param,$defvalue = '') { + if(!isset($_POST[$param])) { + return $defvalue; + } else { + return safedata($_POST[$param]); + } +} + +function _get($param,$defvalue = ''){ + if(!isset($_GET[$param])) { + return $defvalue; + } else { + return safedata($_GET[$param]); + } +} + +require('system/orm.php'); + +ORM::configure("mysql:host=$db_host;dbname=$db_name"); +ORM::configure('username', $db_user); +ORM::configure('password', $db_password); +ORM::configure('driver_options', array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8')); +ORM::configure('return_result_sets', true); +ORM::configure('logging', true); + +$result = ORM::for_table('tbl_appconfig')->find_many(); +foreach($result as $value){ + $config[$value['setting']]=$value['value']; +} + +date_default_timezone_set($config['timezone']); +$_c = $config; + +function _notify($msg,$type='e'){ + $_SESSION['ntype']=$type ; $_SESSION['notify']=$msg ; +} + +require_once('system/vendors/smarty/libs/Smarty.class.php'); +$_theme = APP_URL.'/ui/theme/'.$config['theme']; +$lan_file = 'system/lan/' . $config['language'] . '/common.lan.php'; +require($lan_file); +$ui = new Smarty(); +$ui->setTemplateDir('ui/theme/' . $config['theme'] . '/'); +$ui->setCompileDir('ui/compiled/'); +$ui->setConfigDir('ui/conf/'); +$ui->setCacheDir('ui/cache/'); +$ui->assign('app_url', APP_URL); +define('U', APP_URL.'/index.php?_route='); +$ui->assign('_url', APP_URL.'/index.php?_route='); +$ui->assign('_theme', $_theme); +$ui->assign('_c', $config); +$ui->assign('_L', $_L); +$ui->assign('_system_menu', 'dashboard'); +$ui->assign('_title', $config['CompanyName']); + +function _msglog($type,$msg){ + $_SESSION['ntype'] = $type; + $_SESSION['notify'] = $msg; +} + +if (isset($_SESSION['notify'])) { + $notify = $_SESSION['notify']; + $ntype = $_SESSION['ntype']; + if ($ntype == 's') { + $ui->assign('notify','
+ +
'.$notify.'
'); + } else { + $ui->assign('notify','
+ +
'.$notify.'
'); + } + unset($_SESSION['notify']); + unset($_SESSION['ntype']); +} + +function _autoloader($class) { + if (strpos($class, '_') !== false) { + $class = str_replace('_','/',$class); + include 'autoload/' . $class . '.php'; + } else{ + include 'autoload/' . $class . '.php'; + } +} + +spl_autoload_register('_autoloader'); + +function _auth(){ + if(isset($_SESSION['uid'])){ + return true; + } else{ + r2(U.'login'); + } +} + +function _admin(){ + if(isset($_SESSION['aid'])){ + return true; + } else{ + r2(U.'login'); + } +} + +function _raid($l){ + $r= substr(str_shuffle(str_repeat('0123456789',$l)),0,$l); + return $r; +} + +function _log($description,$type='',$userid='0'){ + $d = ORM::for_table('tbl_logs')->create(); + $d->date = date('Y-m-d H:i:s'); + $d->type = $type; + $d->description = $description; + $d->userid = $userid; + $d->ip = $_SERVER["REMOTE_ADDR"]; + $d->save(); +} + +function time_elapsed_string($datetime, $full = false) { + $now = new DateTime; + $ago = new DateTime($datetime); + $diff = $now->diff($ago); + + $diff->w = floor($diff->d / 7); + $diff->d -= $diff->w * 7; + + $string = array( + 'y' => 'year', + 'm' => 'month', + 'w' => 'week', + 'd' => 'day', + 'h' => 'hour', + 'i' => 'minute', + 's' => 'second', + ); + foreach ($string as $k => &$v) { + if ($diff->$k) { + $v = $diff->$k . ' ' . $v . ($diff->$k > 1 ? 's' : ''); + } else { + unset($string[$k]); + } + } + + if (!$full) $string = array_slice($string, 0, 1); + return $string ? implode(', ', $string) . ' ago' : 'just now'; +} + +// Routing Engine +$req = _get('_route'); +$routes = explode('/', $req); +$handler = $routes['0']; +if ($handler == '') { + $handler = 'default'; +} +$sys_render = 'system/controllers/' . $handler . '.php'; +if (file_exists($sys_render)) { + include($sys_render); +} else { + exit ("$sys_render"); +} diff --git a/system/config.sample.php b/system/config.sample.php new file mode 100644 index 0000000..51b848f --- /dev/null +++ b/system/config.sample.php @@ -0,0 +1,9 @@ + +* @version 5.0 +* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing +* @license GNU General Public License version 2 or later; see LICENSE.txt +* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4 +**/ +_auth(); +$ui->assign('_title', $_L['My_Account'].'- '. $config['CompanyName']); +$ui->assign('_system_menu', 'accounts'); + +$action = $routes['1']; +$user = User::_info(); +$ui->assign('_user', $user); + +use PEAR2\Net\RouterOS; +require_once 'system/autoload/PEAR2/Autoload.php'; + +switch ($action) { + + case 'change-password': + $ui->display('user-change-password.tpl'); + break; + + case 'change-password-post': + $password = _post('password'); + if($password != ''){ + $d = ORM::for_table('tbl_customers')->where('username',$user['username'])->find_one(); + if($d){ + $d_pass = $d['password']; + $npass = _post('npass'); + $cnpass = _post('cnpass'); + + if(Password::_uverify($password,$d_pass) == true){ + if(!Validator::Length($npass,15,2)){ + r2(U.'accounts/change-password','e','New Password must be 3 to 14 character'); + } + if($npass != $cnpass){ + r2(U.'accounts/change-password','e','Both Password should be same'); + } + + $c = ORM::for_table('tbl_user_recharges')->where('username',$user['username'])->find_one(); + if ($c){ + $mikrotik = Router::_info($c['routers']); + if($c['type'] == 'Hotspot'){ + try { + $client = new RouterOS\Client($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + } catch (Exception $e) { + die('Unable to connect to the router.'); + } + $printRequest = new RouterOS\Request('/ip/hotspot/user/print'); + $printRequest->setArgument('.proplist', '.id'); + $printRequest->setQuery(RouterOS\Query::where('name', $user['username'])); + $id = $client->sendSync($printRequest)->getProperty('.id'); + + $setRequest = new RouterOS\Request('/ip/hotspot/user/set'); + $setRequest->setArgument('numbers', $id); + $setRequest->setArgument('password', $npass); + $client->sendSync($setRequest); + + //remove hotspot active + $onlineRequest = new RouterOS\Request('/ip/hotspot/active/print'); + $onlineRequest->setArgument('.proplist', '.id'); + $onlineRequest->setQuery(RouterOS\Query::where('user', $user['username'])); + $id = $client->sendSync($onlineRequest)->getProperty('.id'); + + $removeRequest = new RouterOS\Request('/ip/hotspot/active/remove'); + $removeRequest->setArgument('numbers', $id); + $client->sendSync($removeRequest); + + $d->password = $npass; + $d->save(); + + _msglog('s',$_L['Password_Changed_Successfully']); + _log('['.$user['username'].']: Password changed successfully','User',$user['id']); + + r2(U.'login'); + + }else{ + try { + $client = new RouterOS\Client($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + } catch (Exception $e) { + die('Unable to connect to the router.'); + } + $printRequest = new RouterOS\Request('/ppp/secret/print'); + $printRequest->setArgument('.proplist', '.id'); + $printRequest->setQuery(RouterOS\Query::where('name', $user['username'])); + $id = $client->sendSync($printRequest)->getProperty('.id'); + + $setRequest = new RouterOS\Request('/ppp/secret/set'); + $setRequest->setArgument('numbers', $id); + $setRequest->setArgument('password', $npass); + $client->sendSync($setRequest); + + //remove pppoe active + $onlineRequest = new RouterOS\Request('/ppp/active/print'); + $onlineRequest->setArgument('.proplist', '.id'); + $onlineRequest->setQuery(RouterOS\Query::where('name', $user['username'])); + $id = $client->sendSync($onlineRequest)->getProperty('.id'); + + $removeRequest = new RouterOS\Request('/ppp/active/remove'); + $removeRequest->setArgument('numbers', $id); + $client->sendSync($removeRequest); + + $d->password = $npass; + $d->save(); + + _msglog('s',$_L['Password_Changed_Successfully']); + _log('['.$user['username'].']: Password changed successfully','User',$user['id']); + + r2(U.'login'); + } + }else{ + $d->password = $npass; + $d->save(); + + _msglog('s',$_L['Password_Changed_Successfully']); + _log('['.$user['username'].']: Password changed successfully','User',$user['id']); + + r2(U.'login'); + } + + }else{ + r2(U.'accounts/change-password','e',$_L['Incorrect_Current_Password']); + } + }else{ + r2(U.'accounts/change-password','e',$_L['Incorrect_Current_Password']); + } + }else{ + r2(U.'accounts/change-password','e',$_L['Incorrect_Current_Password']); + } + break; + + case 'profile': + + $id = $_SESSION['uid']; + $d = ORM::for_table('tbl_customers')->find_one($id); + if($d){ + $ui->assign('d',$d); + $ui->display('user-profile.tpl'); + }else{ + r2(U . 'accounts/users', 'e', $_L['Account_Not_Found']); + } + break; + + case 'edit-profile-post': + $fullname = _post('fullname'); + $address = _post('address'); + $phonenumber = _post('phonenumber'); + + $msg = ''; + if(Validator::Length($fullname,31,2) == false){ + $msg .= 'Full Name should be between 3 to 30 characters'. '
'; + } + if(Validator::UnsignedNumber($phonenumber) == false){ + $msg .= 'Phone Number must be a number'. '
'; + } + + $id = _post('id'); + $d = ORM::for_table('tbl_customers')->find_one($id); + if($d){ + }else{ + $msg .= $_L['Data_Not_Found']. '
'; + } + + if($msg == ''){ + $d->fullname = $fullname; + $d->address = $address; + $d->phonenumber = $phonenumber; + $d->save(); + + _log('['.$user['username'].']: '.$_L['User_Updated_Successfully'],'User',$user['id']); + r2(U . 'accounts/profile', 's', $_L['User_Updated_Successfully']); + }else{ + r2(U . 'accounts/profile', 'e', $msg); + } + break; + + default: + echo 'action not defined'; +} \ No newline at end of file diff --git a/system/controllers/admin.php b/system/controllers/admin.php new file mode 100644 index 0000000..ab3532d --- /dev/null +++ b/system/controllers/admin.php @@ -0,0 +1,54 @@ + +* @version 5.0 +* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing +* @license GNU General Public License version 2 or later; see LICENSE.txt +* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4 +**/ +if (isset($routes['1'])) { + $do = $routes['1']; +} else { + $do = 'login-display'; +} + +switch($do){ + case 'post': + $username = _post('username'); + $password = _post('password'); + if($username != '' AND $password != ''){ + $d = ORM::for_table('tbl_users')->where('username',$username)->find_one(); + if($d){ + $d_pass = $d['password']; + if(Password::_verify($password,$d_pass) == true){ + $_SESSION['aid'] = $d['id']; + $d->last_login = date('Y-m-d H:i:s'); + $d->save(); + _log($username .' '. $_L['Login_Successful'],'Admin',$d['id']); + r2(U.'dashboard'); + }else{ + _msglog('e',$_L['Invalid_Username_or_Password']); + _log($username .' '. $_L['Failed_Login'],'Admin'); + r2(U.'admin'); + } + }else{ + _msglog('e',$_L['Invalid_Username_or_Password']); + r2(U.'admin'); + } + }else{ + _msglog('e',$_L['Invalid_Username_or_Password']); + r2(U.'admin'); + } + + break; + + case 'login-display': + $ui->display('admin.tpl'); + break; + + default: + $ui->display('admin.tpl'); + break; +} + diff --git a/system/controllers/autoload.php b/system/controllers/autoload.php new file mode 100644 index 0000000..f1d33b1 --- /dev/null +++ b/system/controllers/autoload.php @@ -0,0 +1,45 @@ + +* @version 5.0 +* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing +* @license GNU General Public License version 2 or later; see LICENSE.txt +* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4 +**/ +_admin(); +$ui->assign('_title', $_L['Network'].' - '. $config['CompanyName']); +$ui->assign('_system_menu', 'network'); + +$action = $routes['1']; +$admin = Admin::_info(); +$ui->assign('_admin', $admin); + +switch ($action) { + case 'pool': + $routers = _get('routers'); + $d = ORM::for_table('tbl_pool')->where('routers', $routers)->find_many(); + $ui->assign('d',$d); + + $ui->display('autoload-pool.tpl'); + break; + + case 'server': + $d = ORM::for_table('tbl_routers')->find_many(); + $ui->assign('d',$d); + + $ui->display('autoload-server.tpl'); + break; + + case 'plan': + $server = _post('server'); + $jenis = _post('jenis'); + $d = ORM::for_table('tbl_plans')->where('routers', $server)->where('type', $jenis)->find_many(); + $ui->assign('d',$d); + + $ui->display('autoload.tpl'); + break; + + default: + echo 'action not defined'; +} \ No newline at end of file diff --git a/system/controllers/bandwidth.php b/system/controllers/bandwidth.php new file mode 100644 index 0000000..5f8fdae --- /dev/null +++ b/system/controllers/bandwidth.php @@ -0,0 +1,141 @@ + +* @version 5.0 +* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing +* @license GNU General Public License version 2 or later; see LICENSE.txt +* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4 +**/ +_admin(); +$ui->assign('_title', $_L['Bandwidth_Plans'].' - '. $config['CompanyName']); +$ui->assign('_system_menu', 'services'); + +$action = $routes['1']; +$admin = Admin::_info(); +$ui->assign('_admin', $admin); + +if($admin['user_type'] != 'Admin' AND $admin['user_type'] != 'Sales'){ + r2(U."dashboard",'e',$_L['Do_Not_Access']); +} + +switch ($action) { + case 'list': + $ui->assign('xfooter', ''); + + $name = _post('name'); + if ($name != ''){ + $paginator = Paginator::bootstrap('tbl_bandwidth','name_bw','%'.$name.'%'); + $d = ORM::for_table('tbl_bandwidth')->where_like('name_bw','%'.$name.'%')->offset($paginator['startpoint'])->limit($paginator['limit'])->order_by_desc('id')->find_many(); + }else{ + $paginator = Paginator::bootstrap('tbl_bandwidth'); + $d = ORM::for_table('tbl_bandwidth')->offset($paginator['startpoint'])->limit($paginator['limit'])->order_by_desc('id')->find_many(); + } + + $ui->assign('d',$d); + $ui->assign('paginator',$paginator); + $ui->display('bandwidth.tpl'); + break; + + case 'add': + $ui->display('bandwidth-add.tpl'); + break; + + case 'edit': + $id = $routes['2']; + $d = ORM::for_table('tbl_bandwidth')->find_one($id); + if($d){ + $ui->assign('d',$d); + $ui->display('bandwidth-edit.tpl'); + }else{ + r2(U . 'bandwidth/list', 'e', $_L['Account_Not_Found']); + } + break; + + case 'delete': + $id = $routes['2']; + $d = ORM::for_table('tbl_bandwidth')->find_one($id); + if($d){ + $d->delete(); + r2(U . 'bandwidth/list', 's', $_L['Delete_Successfully']); + } + break; + + case 'add-post': + $name = _post('name'); + $rate_down = _post('rate_down'); + $rate_down_unit = _post('rate_down_unit'); + $rate_up = _post('rate_up'); + $rate_up_unit = _post('rate_up_unit'); + + $msg = ''; + if(Validator::Length($name,16,4) == false){ + $msg .= 'Name should be between 5 to 15 characters'. '
'; + } + + if($rate_down_unit == 'Kbps'){ $unit_rate_down = $rate_down * 1024; }else{ $unit_rate_down = $rate_down * 1048576; } + if($rate_up_unit == 'Kbps'){ $unit_rate_up = $min_up * 1024; }else{ $unit_rate_up = $min_up * 1048576; } + + $d = ORM::for_table('tbl_bandwidth')->where('name_bw',$name)->find_one(); + if($d){ + $msg .= $_L['BW_already_exist']. '
'; + } + + if($msg == ''){ + $d = ORM::for_table('tbl_bandwidth')->create(); + $d->name_bw = $name; + $d->rate_down = $rate_down; + $d->rate_down_unit = $rate_down_unit; + $d->rate_up = $rate_up; + $d->rate_up_unit = $rate_up_unit; + $d->save(); + + r2(U . 'bandwidth/list', 's', $_L['Created_Successfully']); + }else{ + r2(U . 'bandwidth/add', 'e', $msg); + } + break; + + case 'edit-post': + $name = _post('name'); + $rate_down = _post('rate_down'); + $rate_down_unit = _post('rate_down_unit'); + $rate_up = _post('rate_up'); + $rate_up_unit = _post('rate_up_unit'); + + $msg = ''; + if(Validator::Length($name,16,4) == false){ + $msg .= 'Name should be between 5 to 15 characters'. '
'; + } + + $id = _post('id'); + $d = ORM::for_table('tbl_bandwidth')->find_one($id); + if($d){ + }else{ + $msg .= $_L['Data_Not_Found']. '
'; + } + + if($d['name_bw'] != $name){ + $c = ORM::for_table('tbl_bandwidth')->where('name_bw',$name)->find_one(); + if($c){ + $msg .= $_L['BW_already_exist']. '
'; + } + } + + if($msg == ''){ + $d->name_bw = $name; + $d->rate_down = $rate_down; + $d->rate_down_unit = $rate_down_unit; + $d->rate_up = $rate_up; + $d->rate_up_unit = $rate_up_unit; + $d->save(); + + r2(U . 'bandwidth/list', 's', $_L['Updated_Successfully']); + }else{ + r2(U . 'bandwidth/edit/'.$id, 'e', $msg); + } + break; + + default: + echo 'action not defined'; +} \ No newline at end of file diff --git a/system/controllers/customers.php b/system/controllers/customers.php new file mode 100644 index 0000000..8c8fb6e --- /dev/null +++ b/system/controllers/customers.php @@ -0,0 +1,297 @@ + +* @version 5.0 +* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing +* @license GNU General Public License version 2 or later; see LICENSE.txt +* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4 +**/ +_admin(); +$ui->assign('_title', $_L['Customers'].' - '. $config['CompanyName']); +$ui->assign('_system_menu', 'customers'); + +$action = $routes['1']; +$admin = Admin::_info(); +$ui->assign('_admin', $admin); + +use PEAR2\Net\RouterOS; +require_once 'system/autoload/PEAR2/Autoload.php'; + +if($admin['user_type'] != 'Admin' AND $admin['user_type'] != 'Sales'){ + r2(U."dashboard",'e',$_L['Do_Not_Access']); +} + +switch ($action) { + case 'list': + $ui->assign('xfooter', ''); + $username = _post('username'); + if ($username != ''){ + $paginator = Paginator::bootstrap('tbl_customers','username','%'.$username.'%'); + $d = ORM::for_table('tbl_customers')->where_like('username','%'.$username.'%')->offset($paginator['startpoint'])->limit($paginator['limit'])->order_by_desc('id')->find_many(); + }else{ + $paginator = Paginator::bootstrap('tbl_customers'); + $d = ORM::for_table('tbl_customers')->offset($paginator['startpoint'])->limit($paginator['limit'])->order_by_desc('id')->find_many(); + } + + $ui->assign('d',$d); + $ui->assign('paginator',$paginator); + $ui->display('customers.tpl'); + break; + + case 'add': + $ui->display('customers-add.tpl'); + break; + + case 'edit': + $id = $routes['2']; + $d = ORM::for_table('tbl_customers')->find_one($id); + if($d){ + $ui->assign('d',$d); + $ui->display('customers-edit.tpl'); + }else{ + r2(U . 'customers/list', 'e', $_L['Account_Not_Found']); + } + break; + + case 'delete': + $id = $routes['2']; + + $d = ORM::for_table('tbl_customers')->find_one($id); + if($d){ + $c = ORM::for_table('tbl_user_recharges')->where('username',$d['username'])->find_one(); + if ($c){ + $mikrotik = Router::_info($c['routers']); + if($c['type'] == 'Hotspot'){ + try { + $client = new RouterOS\Client($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + } catch (Exception $e) { + die('Unable to connect to the router.'); + } + $printRequest = new RouterOS\Request('/ip/hotspot/user/print'); + $printRequest->setArgument('.proplist', '.id'); + $printRequest->setQuery(RouterOS\Query::where('name', $c['username'])); + $id = $client->sendSync($printRequest)->getProperty('.id'); + + $setRequest = new RouterOS\Request('/ip/hotspot/user/remove'); + $setRequest->setArgument('numbers', $id); + $client->sendSync($setRequest); + + //remove hotspot active + $onlineRequest = new RouterOS\Request('/ip/hotspot/active/print'); + $onlineRequest->setArgument('.proplist', '.id'); + $onlineRequest->setQuery(RouterOS\Query::where('user', $c['username'])); + $id = $client->sendSync($onlineRequest)->getProperty('.id'); + + $removeRequest = new RouterOS\Request('/ip/hotspot/active/remove'); + $removeRequest->setArgument('numbers', $id); + $client->sendSync($removeRequest); + + }else{ + + try { + $client = new RouterOS\Client($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + } catch (Exception $e) { + die('Unable to connect to the router.'); + } + $printRequest = new RouterOS\Request('/ppp/secret/print'); + $printRequest->setArgument('.proplist', '.id'); + $printRequest->setQuery(RouterOS\Query::where('name', $c['username'])); + $id = $client->sendSync($printRequest)->getProperty('.id'); + + $setRequest = new RouterOS\Request('/ppp/secret/remove'); + $setRequest->setArgument('numbers', $id); + $client->sendSync($setRequest); + + //remove pppoe active + $onlineRequest = new RouterOS\Request('/ppp/active/print'); + $onlineRequest->setArgument('.proplist', '.id'); + $onlineRequest->setQuery(RouterOS\Query::where('name', $c['username'])); + $id = $client->sendSync($onlineRequest)->getProperty('.id'); + + $removeRequest = new RouterOS\Request('/ppp/active/remove'); + $removeRequest->setArgument('numbers', $id); + $client->sendSync($removeRequest); + } + $d->delete(); + $c->delete(); + }else{ + $d->delete(); + $c->delete(); + } + + r2(U . 'customers/list', 's', $_L['User_Delete_Ok']); + } + break; + + case 'add-post': + $username = _post('username'); + $fullname = _post('fullname'); + $password = _post('password'); + $cpassword = _post('cpassword'); + $address = _post('address'); + $phonenumber = _post('phonenumber'); + + $msg = ''; + if(Validator::Length($username,35,2) == false){ + $msg .= 'Username should be between 3 to 55 characters'. '
'; + } + if(Validator::Length($fullname,36,2) == false){ + $msg .= 'Full Name should be between 3 to 25 characters'. '
'; + } + if(!Validator::Length($password,35,2)){ + $msg .= 'Password should be between 3 to 35 characters'. '
'; + + } + if($password != $cpassword){ + $msg .= 'Passwords does not match'. '
'; + } + + $d = ORM::for_table('tbl_customers')->where('username',$username)->find_one(); + if($d){ + $msg .= $_L['account_already_exist']. '
'; + } + + if($msg == ''){ + $d = ORM::for_table('tbl_customers')->create(); + $d->username = $username; + $d->password = $password; + $d->fullname = $fullname; + $d->address = $address; + $d->phonenumber = $phonenumber; + $d->save(); + r2(U . 'customers/list', 's', $_L['account_created_successfully']); + }else{ + r2(U . 'customers/add', 'e', $msg); + } + break; + + case 'edit-post': + $username = _post('username'); + $fullname = _post('fullname'); + $password = _post('password'); + $cpassword = _post('cpassword'); + $address = _post('address'); + $phonenumber = _post('phonenumber'); + + $msg = ''; + if(Validator::Length($username,16,2) == false){ + $msg .= 'Username should be between 3 to 15 characters'. '
'; + } + if(Validator::Length($fullname,26,2) == false){ + $msg .= 'Full Name should be between 3 to 25 characters'. '
'; + } + if($password != ''){ + if(!Validator::Length($password,15,2)){ + $msg .= 'Password should be between 3 to 15 characters'. '
'; + + } + if($password != $cpassword){ + $msg .= 'Passwords does not match'. '
'; + } + } + + $id = _post('id'); + $d = ORM::for_table('tbl_customers')->find_one($id); + if($d){ + + }else{ + $msg .= $_L['Data_Not_Found']. '
'; + } + + if($d['username'] != $username){ + $c = ORM::for_table('tbl_customers')->where('username',$username)->find_one(); + if($c){ + $msg .= $_L['account_already_exist']. '
'; + } + } + + if($msg == ''){ + $c = ORM::for_table('tbl_user_recharges')->where('username',$username)->find_one(); + if ($c){ + $mikrotik = Router::_info($c['routers']); + if($c['type'] == 'Hotspot'){ + try { + $client = new RouterOS\Client($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + } catch (Exception $e) { + die('Unable to connect to the router.'); + } + $printRequest = new RouterOS\Request('/ip/hotspot/user/print'); + $printRequest->setArgument('.proplist', '.id'); + $printRequest->setQuery(RouterOS\Query::where('name', $c['username'])); + $id = $client->sendSync($printRequest)->getProperty('.id'); + + $setRequest = new RouterOS\Request('/ip/hotspot/user/set'); + $setRequest->setArgument('numbers', $id); + $setRequest->setArgument('password', $password); + $client->sendSync($setRequest); + + //remove hotspot active + $onlineRequest = new RouterOS\Request('/ip/hotspot/active/print'); + $onlineRequest->setArgument('.proplist', '.id'); + $onlineRequest->setQuery(RouterOS\Query::where('user', $c['username'])); + $id = $client->sendSync($onlineRequest)->getProperty('.id'); + + $removeRequest = new RouterOS\Request('/ip/hotspot/active/remove'); + $removeRequest->setArgument('numbers', $id); + $client->sendSync($removeRequest); + + $d->password = $password; + $d->save(); + + }else{ + try { + $client = new RouterOS\Client($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + } catch (Exception $e) { + die('Unable to connect to the router.'); + } + $printRequest = new RouterOS\Request('/ppp/secret/print'); + $printRequest->setArgument('.proplist', '.id'); + $printRequest->setQuery(RouterOS\Query::where('name', $c['username'])); + $id = $client->sendSync($printRequest)->getProperty('.id'); + + $setRequest = new RouterOS\Request('/ppp/secret/set'); + $setRequest->setArgument('numbers', $id); + $setRequest->setArgument('password', $password); + $client->sendSync($setRequest); + + //remove pppoe active + $onlineRequest = new RouterOS\Request('/ppp/active/print'); + $onlineRequest->setArgument('.proplist', '.id'); + $onlineRequest->setQuery(RouterOS\Query::where('name', $c['username'])); + $id = $client->sendSync($onlineRequest)->getProperty('.id'); + + $removeRequest = new RouterOS\Request('/ppp/active/remove'); + $removeRequest->setArgument('numbers', $id); + $client->sendSync($removeRequest); + + $d->password = $password; + $d->save(); + } + $d->username = $username; + if($password != ''){ + $d->password = $password; + } + $d->fullname = $fullname; + $d->address = $address; + $d->phonenumber = $phonenumber; + $d->save(); + }else{ + $d->username = $username; + if($password != ''){ + $d->password = $password; + } + $d->fullname = $fullname; + $d->address = $address; + $d->phonenumber = $phonenumber; + $d->save(); + } + r2(U . 'customers/list', 's', 'User Updated Successfully'); + }else{ + r2(U . 'customers/edit/'.$id, 'e', $msg); + } + break; + + default: + echo 'action not defined'; +} \ No newline at end of file diff --git a/system/controllers/dashboard.php b/system/controllers/dashboard.php new file mode 100644 index 0000000..5eec56d --- /dev/null +++ b/system/controllers/dashboard.php @@ -0,0 +1,59 @@ + +* @version 5.0 +* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing +* @license GNU General Public License version 2 or later; see LICENSE.txt +* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4 +**/ +_admin(); +$ui->assign('_title', $_L['Dashboard'].' - '. $config['CompanyName']); +$admin = Admin::_info(); +$ui->assign('_admin', $admin); + +if($admin['user_type'] != 'Admin' AND $admin['user_type'] != 'Sales'){ + r2(U."home",'e',$_L['Do_Not_Access']); +} + +$fdate = date('Y-m-01'); +$tdate = date('Y-m-t'); +//first day of month +$first_day_month = date('Y-m-01'); +$mdate = date('Y-m-d'); +$month_n = date('n'); + +$iday = ORM::for_table('tbl_transactions')->where('recharged_on',$mdate)->sum('price'); +if($iday == ''){ + $iday = '0.00'; +} +$ui->assign('iday',$iday); + +$imonth = ORM::for_table('tbl_transactions')->where_gte('recharged_on',$first_day_month)->where_lte('recharged_on',$mdate)->sum('price'); +if($imonth == ''){ + $imonth = '0.00'; +} +$ui->assign('imonth',$imonth); + +$u_act = ORM::for_table('tbl_user_recharges')->where('status','on')->count(); +if($u_act == ''){ + $u_act = '0'; +} +$ui->assign('u_act',$u_act); + +$u_all = ORM::for_table('tbl_user_recharges')->count(); +if($u_all == ''){ + $u_all = '0'; +} +$ui->assign('u_all',$u_all); +//user expire +$expire = ORM::for_table('tbl_user_recharges')->where('expiration',$mdate)->order_by_desc('id')->find_many(); +$ui->assign('expire',$expire); + +//activity log +$dlog = ORM::for_table('tbl_logs')->limit(5)->order_by_desc('id')->find_many(); +$ui->assign('dlog',$dlog); +$log = ORM::for_table('tbl_logs')->count(); +$ui->assign('log',$log); + +$ui->display('dashboard.tpl'); \ No newline at end of file diff --git a/system/controllers/default.php b/system/controllers/default.php new file mode 100644 index 0000000..e77ae54 --- /dev/null +++ b/system/controllers/default.php @@ -0,0 +1,11 @@ + +* @version 5.0 +* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing +* @license GNU General Public License version 2 or later; see LICENSE.txt +* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4 +**/ + +r2(APP_URL.'/index.php?_route=dashboard'); diff --git a/system/controllers/export.php b/system/controllers/export.php new file mode 100644 index 0000000..ee84440 --- /dev/null +++ b/system/controllers/export.php @@ -0,0 +1,355 @@ + +* @version 5.0 +* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing +* @license GNU General Public License version 2 or later; see LICENSE.txt +* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4 +**/ +_admin(); +$ui->assign('_title', $_L['Reports'].'- '. $config['CompanyName']); +$ui->assign('_sysfrm_menu', 'reports'); + +$action = $routes['1']; +$admin = Admin::_info(); +$ui->assign('_admin', $admin); + +$mdate = date('Y-m-d'); +$tdate = date('Y-m-d', strtotime('today - 30 days')); + +//first day of month +$first_day_month = date('Y-m-01'); +// +$this_week_start = date('Y-m-d',strtotime( 'previous sunday')); +// 30 days before +$before_30_days = date('Y-m-d', strtotime('today - 30 days')); +//this month +$month_n = date('n'); + +switch ($action) { + + case 'print-by-date': + $mdate = date('Y-m-d'); + $d = ORM::for_table('tbl_transactions'); + $d->where('recharged_on', $mdate); + $d->order_by_desc('id'); + $x = $d->find_many(); + + $dr = ORM::for_table('tbl_transactions'); + $dr->where('recharged_on', $mdate); + $dr->order_by_desc('id'); + $xy = $dr->sum('price'); + + $ui->assign('d',$x); + $ui->assign('dr',$xy); + $ui->assign('mdate',$mdate); + $ui->assign('recharged_on',$mdate); + + $ui->display('print-by-date.tpl'); + break; + + case 'pdf-by-date': + $mdate = date('Y-m-d'); + + $d = ORM::for_table('tbl_transactions'); + $d->where('recharged_on', $mdate); + $d->order_by_desc('id'); + $x = $d->find_many(); + + $dr = ORM::for_table('tbl_transactions'); + $dr->where('recharged_on', $mdate); + $dr->order_by_desc('id'); + $xy = $dr->sum('price'); + + $title = ' Reports ['.$mdate.']'; + $title = str_replace('-',' ',$title); + + if ($x) { + $html = ' +
+
+

'.$config['CompanyName'].'

+ '.$config['address'].'
+ '.$_L['Phone_Number'].': '.$config['phone'].'
+
+ +
+ + + + + + + + + + + + '; + $c = true; + foreach ($x as $value) { + + $username = $value['username']; + $plan_name = $value['plan_name']; + $type = $value['type']; + $price = $_c['currency_code'].' '. number_format($value['price'],0,$_c['dec_point'],$_c['thousands_sep']); + $recharged_on = date( $config['date_format'], strtotime($value['recharged_on'])); + $expiration = date( $config['date_format'], strtotime($value['expiration'])); + $time = $value['time']; + $method = $value['method']; + $routers = $value['routers']; + + $html .= ""." + + + + + + + + + "; + } + $html .= '
'.$_L['Username'].''.$_L['Plan_Name'].''.$_L['Type'].''.$_L['Plan_Price'].''.$_L['Created_On'].''.$_L['Expires_On'].''.$_L['Method'].''.$_L['Routers'].'
$username$plan_name$type$price$recharged_on $time $expiration $time $method$routers
+

'.$_L['Total_Income'].':

+

'.$_c['currency_code'].' '.number_format($xy,2,$_c['dec_point'],$_c['thousands_sep']).'

'; + + define('_MPDF_PATH','system/vendors/mpdf/'); + + require('system/vendors/mpdf/mpdf.php'); + + $mpdf=new mPDF('c','A4','','',20,15,25,25,10,10); + $mpdf->SetProtection(array('print')); + $mpdf->SetTitle($config['CompanyName'].' Reports'); + $mpdf->SetAuthor($config['CompanyName']); + $mpdf->SetWatermarkText($d['price']); + $mpdf->showWatermarkText = true; + $mpdf->watermark_font = 'Helvetica'; + $mpdf->watermarkTextAlpha = 0.1; + $mpdf->SetDisplayMode('fullpage'); + + $style = ''; + + $nhtml = <<WriteHTML($nhtml); + $mpdf->Output(date('Y-m-d')._raid(4).'.pdf', 'D'); + + }else{ + echo 'No Data'; + } + + break; + + case 'print-by-period': + $fdate = _post('fdate'); + $tdate = _post('tdate'); + $stype = _post('stype'); + + $d = ORM::for_table('tbl_transactions'); + if ($stype != ''){ + $d->where('type', $stype); + } + + $d->where_gte('recharged_on', $fdate); + $d->where_lte('recharged_on', $tdate); + $d->order_by_desc('id'); + $x = $d->find_many(); + + $dr = ORM::for_table('tbl_transactions'); + if ($stype != ''){ + $dr->where('type', $stype); + } + + $dr->where_gte('recharged_on', $fdate); + $dr->where_lte('recharged_on', $tdate); + $xy = $dr->sum('price'); + + $ui->assign('d',$x); + $ui->assign('dr',$xy); + $ui->assign('fdate',$fdate); + $ui->assign('tdate',$tdate); + $ui->assign('stype',$stype); + + $ui->display('print-by-period.tpl'); + break; + + + case 'pdf-by-period': + $fdate = _post('fdate'); + $tdate = _post('tdate'); + $stype = _post('stype'); + + $d = ORM::for_table('tbl_transactions'); + if ($stype != ''){ + $d->where('type', $stype); + } + + $d->where_gte('recharged_on', $fdate); + $d->where_lte('recharged_on', $tdate); + $d->order_by_desc('id'); + $x = $d->find_many(); + + $dr = ORM::for_table('tbl_transactions'); + if ($stype != ''){ + $dr->where('type', $stype); + } + + $dr->where_gte('recharged_on', $fdate); + $dr->where_lte('recharged_on', $tdate); + $xy = $dr->sum('price'); + + $title = ' Reports ['.$mdate.']'; + $title = str_replace('-',' ',$title); + + if ($x) { + $html = ' +
+
+

'.$config['CompanyName'].'

+ '.$config['address'].'
+ '.$_L['Phone_Number'].': '.$config['phone'].'
+
+ +
+ + + + + + + + + + + + '; + $c = true; + foreach ($x as $value) { + + $username = $value['username']; + $plan_name = $value['plan_name']; + $type = $value['type']; + $price = $_c['currency_code'].' '. number_format($value['price'],0,$_c['dec_point'],$_c['thousands_sep']); + $recharged_on = date( $config['date_format'], strtotime($value['recharged_on'])); + $expiration = date( $config['date_format'], strtotime($value['expiration'])); + $time = $value['time']; + $method = $value['method']; + $routers = $value['routers']; + + $html .= ""." + + + + + + + + + "; + } + $html .= '
'.$_L['Username'].''.$_L['Plan_Name'].''.$_L['Type'].''.$_L['Plan_Price'].''.$_L['Created_On'].''.$_L['Expires_On'].''.$_L['Method'].''.$_L['Routers'].'
$username$plan_name$type$price$recharged_on $time $expiration $time $method$routers
+

'.$_L['Total_Income'].':

+

'.$_c['currency_code'].' '.number_format($xy,2,$_c['dec_point'],$_c['thousands_sep']).'

'; + + define('_MPDF_PATH','system/vendors/mpdf/'); + + require('system/vendors/mpdf/mpdf.php'); + + $mpdf=new mPDF('c','A4','','',20,15,25,25,10,10); + $mpdf->SetProtection(array('print')); + $mpdf->SetTitle($config['CompanyName'].' Reports'); + $mpdf->SetAuthor($config['CompanyName']); + $mpdf->SetWatermarkText($d['price']); + $mpdf->showWatermarkText = true; + $mpdf->watermark_font = 'Helvetica'; + $mpdf->watermarkTextAlpha = 0.1; + $mpdf->SetDisplayMode('fullpage'); + + $style = ''; + + $nhtml = <<WriteHTML($nhtml); + $mpdf->Output(date('Y-m-d')._raid(4).'.pdf', 'D'); + + }else{ + echo 'No Data'; + } + + break; + + default: + echo 'action not defined'; +} \ No newline at end of file diff --git a/system/controllers/home.php b/system/controllers/home.php new file mode 100644 index 0000000..7df203a --- /dev/null +++ b/system/controllers/home.php @@ -0,0 +1,20 @@ + +* @version 5.0 +* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing +* @license GNU General Public License version 2 or later; see LICENSE.txt +* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4 +**/ +_auth(); +$ui->assign('_title', $_L['Dashboard'].' - '. $config['CompanyName']); + +$user = User::_info(); +$ui->assign('_user', $user); + +//Client Page +$bill = User::_billing(); +$ui->assign('_bill', $bill); + +$ui->display('user-dashboard.tpl'); \ No newline at end of file diff --git a/system/controllers/index.html b/system/controllers/index.html new file mode 100644 index 0000000..9757970 --- /dev/null +++ b/system/controllers/index.html @@ -0,0 +1,8 @@ + + + 403 Forbidden + + +

Directory access is forbidden.

+ + \ No newline at end of file diff --git a/system/controllers/login.php b/system/controllers/login.php new file mode 100644 index 0000000..c3b7763 --- /dev/null +++ b/system/controllers/login.php @@ -0,0 +1,55 @@ + +* @version 5.0 +* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing +* @license GNU General Public License version 2 or later; see LICENSE.txt +* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4 +**/ + +if (isset($routes['1'])) { + $do = $routes['1']; +} else { + $do = 'login-display'; +} + +switch($do){ + case 'post': + $username = _post('username'); + $password = _post('password'); + if($username != '' AND $password != ''){ + $d = ORM::for_table('tbl_customers')->where('username',$username)->find_one(); + if($d){ + $d_pass = $d['password']; + if(Password::_uverify($password,$d_pass) == true){ + $_SESSION['uid'] = $d['id']; + $d->last_login = date('Y-m-d H:i:s'); + $d->save(); + _log($username .' '. $_L['Login_Successful'],'User',$d['id']); + r2(U.'home'); + }else{ + _msglog('e',$_L['Invalid_Username_or_Password']); + _log($username .' '. $_L['Failed_Login'],'User'); + r2(U.'login'); + } + }else{ + _msglog('e',$_L['Invalid_Username_or_Password']); + r2(U.'login'); + } + }else{ + _msglog('e',$_L['Invalid_Username_or_Password']); + r2(U.'login'); + } + + break; + + case 'login-display': + $ui->display('login.tpl'); + break; + + default: + $ui->display('login.tpl'); + break; +} + diff --git a/system/controllers/logout.php b/system/controllers/logout.php new file mode 100644 index 0000000..86c7896 --- /dev/null +++ b/system/controllers/logout.php @@ -0,0 +1,12 @@ + +* @version 5.0 +* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing +* @license GNU General Public License version 2 or later; see LICENSE.txt +* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4 +**/ +session_start(); +session_destroy(); +header('location: index.php'); \ No newline at end of file diff --git a/system/controllers/message.php b/system/controllers/message.php new file mode 100644 index 0000000..bc588f9 --- /dev/null +++ b/system/controllers/message.php @@ -0,0 +1,21 @@ + +* @version 5.0 +* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing +* @license GNU General Public License version 2 or later; see LICENSE.txt +* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4 +**/ +_admin(); +$ui->assign('_title', $_L['Private_Message'].'- '. $config['CompanyName']); +$ui->assign('_system_menu', 'message'); + +$action = $routes['1']; +$admin = Admin::_info(); +$ui->assign('_admin', $admin); + +switch ($action) { + default: + $ui->display('a404.tpl'); +} \ No newline at end of file diff --git a/system/controllers/order.php b/system/controllers/order.php new file mode 100644 index 0000000..48c568f --- /dev/null +++ b/system/controllers/order.php @@ -0,0 +1,20 @@ + +* @version 5.0 +* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing +* @license GNU General Public License version 2 or later; see LICENSE.txt +* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4 +**/ +_auth(); +$ui->assign('_title', $_L['Order_Voucher'].'- '. $config['CompanyName']); +$ui->assign('_system_menu', 'order'); + +$user = User::_info(); +$ui->assign('_user', $user); + +switch ($action) { + default: + $ui->display('404.tpl'); +} \ No newline at end of file diff --git a/system/controllers/pm.php b/system/controllers/pm.php new file mode 100644 index 0000000..7d22fdf --- /dev/null +++ b/system/controllers/pm.php @@ -0,0 +1,21 @@ + +* @version 5.0 +* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing +* @license GNU General Public License version 2 or later; see LICENSE.txt +* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4 +**/ +_auth(); +$ui->assign('_title', $_L['Private_Message'].'- '. $config['CompanyName']); +$ui->assign('_system_menu', 'pm'); + +$action = $routes['1']; +$user = User::_info(); +$ui->assign('_user', $user); + +switch ($action) { + default: + $ui->display('404.tpl'); +} \ No newline at end of file diff --git a/system/controllers/pool.php b/system/controllers/pool.php new file mode 100644 index 0000000..4b7cd13 --- /dev/null +++ b/system/controllers/pool.php @@ -0,0 +1,185 @@ + +* @version 5.0 +* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing +* @license GNU General Public License version 2 or later; see LICENSE.txt +* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4 +**/ +_admin(); +$ui->assign('_title', $_L['Network'].' - '. $config['CompanyName']); +$ui->assign('_system_menu', 'network'); + +$action = $routes['1']; +$admin = Admin::_info(); +$ui->assign('_admin', $admin); + +if($admin['user_type'] != 'Admin'){ + r2(U."dashboard",'e',$_L['Do_Not_Access']); +} + +use PEAR2\Net\RouterOS; +require_once 'system/autoload/PEAR2/Autoload.php'; + +switch ($action) { + case 'list': + $ui->assign('xfooter', ''); + + $name = _post('name'); + if ($name != ''){ + $paginator = Paginator::bootstrap('tbl_pool','pool_name','%'.$name.'%'); + $d = ORM::for_table('tbl_pool')->where_like('pool_name','%'.$name.'%')->offset($paginator['startpoint'])->limit($paginator['limit'])->order_by_desc('id')->find_many(); + }else{ + $paginator = Paginator::bootstrap('tbl_pool'); + $d = ORM::for_table('tbl_pool')->offset($paginator['startpoint'])->limit($paginator['limit'])->order_by_desc('id')->find_many(); + } + + $ui->assign('d',$d); + $ui->assign('paginator',$paginator); + $ui->display('pool.tpl'); + break; + + case 'add': + $r = ORM::for_table('tbl_routers')->find_many(); + $ui->assign('r',$r); + + $ui->display('pool-add.tpl'); + break; + + case 'edit': + $id = $routes['2']; + $d = ORM::for_table('tbl_pool')->find_one($id); + if($d){ + $ui->assign('d',$d); + $ui->display('pool-edit.tpl'); + }else{ + r2(U . 'pool/list', 'e', $_L['Account_Not_Found']); + } + break; + + case 'delete': + $id = $routes['2']; + + $d = ORM::for_table('tbl_pool')->find_one($id); + $mikrotik = Router::_info($d['routers']); + if($d){ + try { + $client = new RouterOS\Client($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + } catch (Exception $e) { + die('Unable to connect to the router.'); + } + $printRequest = new RouterOS\Request( + '/ip pool print .proplist=name', + RouterOS\Query::where('name', $d['pool_name']) + ); + $poolName = $client->sendSync($printRequest)->getProperty('name'); + + $removeRequest = new RouterOS\Request('/ip/pool/remove'); + $client($removeRequest + ->setArgument('numbers', $poolName) + ); + + $d->delete(); + + r2(U . 'pool/list', 's', $_L['Delete_Successfully']); + } + break; + + case 'add-post': + $name = _post('name'); + $ip_address = _post('ip_address'); + $routers = _post('routers'); + + $msg = ''; + if(Validator::Length($name,30,2) == false){ + $msg .= 'Name should be between 3 to 30 characters'. '
'; + } + if ($ip_address == '' OR $routers == ''){ + $msg .= $_L['All_field_is_required']. '
'; + } + + $d = ORM::for_table('tbl_pool')->where('pool_name',$name)->find_one(); + if($d){ + $msg .= $_L['Pool_already_exist']. '
'; + } + $mikrotik = Router::_info($routers); + if($msg == ''){ + try { + $client = new RouterOS\Client($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + } catch (Exception $e) { + die('Unable to connect to the router.'); + } + $addRequest = new RouterOS\Request('/ip/pool/add'); + $client->sendSync($addRequest + ->setArgument('name', $name) + ->setArgument('ranges', $ip_address) + ); + + $b = ORM::for_table('tbl_pool')->create(); + $b->pool_name = $name; + $b->range_ip = $ip_address; + $b->routers = $routers; + $b->save(); + + r2(U . 'pool/list', 's', $_L['Created_Successfully']); + }else{ + r2(U . 'pool/add', 'e', $msg); + } + break; + + + case 'edit-post': + $name = _post('name'); + $ip_address = _post('ip_address'); + $routers = _post('routers'); + + $msg = ''; + if(Validator::Length($name,30,2) == false){ + $msg .= 'Name should be between 3 to 30 characters'. '
'; + } + if ($ip_address == '' OR $routers == ''){ + $msg .= $_L['All_field_is_required']. '
'; + } + + $id = _post('id'); + $d = ORM::for_table('tbl_pool')->find_one($id); + if($d){ + + }else{ + $msg .= $_L['Data_Not_Found']. '
'; + } + + $mikrotik = Router::_info($routers); + if($msg == ''){ + try { + $client = new RouterOS\Client($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + } catch (Exception $e) { + die('Unable to connect to the router.'); + } + $printRequest = new RouterOS\Request( + '/ip pool print .proplist=name', + RouterOS\Query::where('name', $name) + ); + $poolName = $client->sendSync($printRequest)->getProperty('name'); + + $setRequest = new RouterOS\Request('/ip/pool/set'); + $client($setRequest + ->setArgument('numbers', $poolName) + ->setArgument('ranges', $ip_address) + ); + + $d->pool_name = $name; + $d->range_ip = $ip_address; + $d->routers = $routers; + $d->save(); + + r2(U . 'pool/list', 's', $_L['Updated_Successfully']); + }else{ + r2(U . 'pool/edit/'.$id, 'e', $msg); + } + break; + + default: + echo 'action not defined'; +} \ No newline at end of file diff --git a/system/controllers/prepaid.php b/system/controllers/prepaid.php new file mode 100644 index 0000000..9302621 --- /dev/null +++ b/system/controllers/prepaid.php @@ -0,0 +1,702 @@ + +* @version 5.0 +* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing +* @license GNU General Public License version 2 or later; see LICENSE.txt +* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4 +**/ +_admin(); +$ui->assign('_title', $_L['Recharge_Account'].' - '. $config['CompanyName']); +$ui->assign('_system_menu', 'prepaid'); + +$action = $routes['1']; +$admin = Admin::_info(); +$ui->assign('_admin', $admin); + +if($admin['user_type'] != 'Admin' AND $admin['user_type'] != 'Sales'){ + r2(U."dashboard",'e',$_L['Do_Not_Access']); +} + +use PEAR2\Net\RouterOS; +require_once 'system/autoload/PEAR2/Autoload.php'; + +switch ($action) { + case 'list': + $ui->assign('xfooter', ''); + + $username = _post('username'); + if ($username != ''){ + $paginator = Paginator::bootstrap('tbl_user_recharges','username','%'.$username.'%'); + $d = ORM::for_table('tbl_user_recharges')->where_like('username','%'.$username.'%')->offset($paginator['startpoint'])->limit($paginator['limit'])->order_by_desc('id')->find_many(); + }else{ + $paginator = Paginator::bootstrap('tbl_user_recharges'); + $d = ORM::for_table('tbl_user_recharges')->offset($paginator['startpoint'])->limit($paginator['limit'])->order_by_desc('id')->find_many(); + } + + $ui->assign('d',$d); + $ui->assign('paginator',$paginator); + $ui->display('prepaid.tpl'); + break; + + case 'recharge': + $c = ORM::for_table('tbl_customers')->find_many(); + $ui->assign('c',$c); + $p = ORM::for_table('tbl_plans')->find_many(); + $ui->assign('p',$p); + $r = ORM::for_table('tbl_routers')->find_many(); + $ui->assign('r',$r); + + $ui->display('recharge.tpl'); + break; + + case 'recharge-user': + $id = $routes['2']; + $ui->assign('id',$id); + + $c = ORM::for_table('tbl_customers')->find_many(); + $ui->assign('c',$c); + $p = ORM::for_table('tbl_plans')->find_many(); + $ui->assign('p',$p); + $r = ORM::for_table('tbl_routers')->find_many(); + $ui->assign('r',$r); + + $ui->display('recharge-user.tpl'); + break; + + case 'recharge-post': + $id_customer = _post('id_customer'); + $type = _post('type'); + $server = _post('server'); + $plan = _post('plan'); + + $date_now = date("Y-m-d H:i:s"); + $date_only = date("Y-m-d"); + $time = date("H:i:s"); + + $msg = ''; + if ($id_customer == '' OR $type == '' OR $server == '' OR $plan == ''){ + $msg .= 'All field is required'. '
'; + } + + if($msg == ''){ + $c = ORM::for_table('tbl_customers')->where('id',$id_customer)->find_one(); + $p = ORM::for_table('tbl_plans')->where('id',$plan)->find_one(); + $b = ORM::for_table('tbl_user_recharges')->where('customer_id',$id_customer)->find_one(); + + $mikrotik = Router::_info($server); + $date_exp = date("Y-m-d", mktime(0,0,0,date("m"),date("d") + $p['validity'],date("Y"))); + + if($type == 'Hotspot'){ + if($b){ + try { + $client = new RouterOS\Client($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + } catch (Exception $e) { + die('Unable to connect to the router.'); + } + $printRequest = new RouterOS\Request( + '/ip hotspot user print .proplist=name', + RouterOS\Query::where('name', $c['username']) + ); + $userName = $client->sendSync($printRequest)->getProperty('name'); + $removeRequest = new RouterOS\Request('/ip/hotspot/user/remove'); + $client($removeRequest + ->setArgument('numbers', $userName) + ); + $addRequest = new RouterOS\Request('/ip/hotspot/user/add'); + $client->sendSync($addRequest + ->setArgument('name', $c['username']) + ->setArgument('profile', $p['name_plan']) + ->setArgument('password', $c['password']) + ); + + $b->customer_id = $id_customer; + $b->username = $c['username']; + $b->plan_id = $plan; + $b->namebp = $p['name_plan']; + $b->recharged_on = $date_only; + $b->expiration = $date_exp; + $b->time = $time; + $b->status = "on"; + $b->method = "admin"; + $b->routers = $server; + $b->type = "Hotspot"; + $b->save(); + + // insert table transactions + $t = ORM::for_table('tbl_transactions')->create(); + $t->invoice = "INV-"._raid(5); + $t->username = $c['username']; + $t->plan_name = $p['name_plan']; + $t->price = $p['price']; + $t->recharged_on = $date_only; + $t->expiration = $date_exp; + $t->time = $time; + $t->method = "admin"; + $t->routers = $server; + $t->type = "Hotspot"; + $t->save(); + + }else{ + try { + $client = new RouterOS\Client($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + } catch (Exception $e) { + die('Unable to connect to the router.'); + } + $addRequest = new RouterOS\Request('/ip/hotspot/user/add'); + $client->sendSync($addRequest + ->setArgument('name', $c['username']) + ->setArgument('profile', $p['name_plan']) + ->setArgument('password', $c['password']) + ); + + $d = ORM::for_table('tbl_user_recharges')->create(); + $d->customer_id = $id_customer; + $d->username = $c['username']; + $d->plan_id = $plan; + $d->namebp = $p['name_plan']; + $d->recharged_on = $date_only; + $d->expiration = $date_exp; + $d->time = $time; + $d->status = "on"; + $d->method = "admin"; + $d->routers = $server; + $d->type = "Hotspot"; + $d->save(); + + // insert table transactions + $t = ORM::for_table('tbl_transactions')->create(); + $t->invoice = "INV-"._raid(5); + $t->username = $c['username']; + $t->plan_name = $p['name_plan']; + $t->price = $p['price']; + $t->recharged_on = $date_only; + $t->expiration = $date_exp; + $t->time = $time; + $t->method = "admin"; + $t->routers = $server; + $t->type = "Hotspot"; + $t->save(); + } + + }else{ + + if($b){ + try { + $client = new RouterOS\Client($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + } catch (Exception $e) { + die('Unable to connect to the router.'); + } + $printRequest = new RouterOS\Request( + '/ppp secret print .proplist=name', + RouterOS\Query::where('name', $c['username']) + ); + $userName = $client->sendSync($printRequest)->getProperty('name'); + + $removeRequest = new RouterOS\Request('/ppp/secret/remove'); + $client($removeRequest + ->setArgument('numbers', $userName) + ); + + $addRequest = new RouterOS\Request('/ppp/secret/add'); + $client->sendSync($addRequest + ->setArgument('name', $c['username']) + ->setArgument('service', 'pppoe') + ->setArgument('profile', $p['name_plan']) + ->setArgument('password', $c['password']) + ); + + $b->customer_id = $id_customer; + $b->username = $c['username']; + $b->plan_id = $plan; + $b->namebp = $p['name_plan']; + $b->recharged_on = $date_only; + $b->expiration = $date_exp; + $b->time = $time; + $b->status = "on"; + $b->method = "admin"; + $b->routers = $server; + $b->type = "PPPOE"; + $b->save(); + + // insert table transactions + $t = ORM::for_table('tbl_transactions')->create(); + $t->invoice = "INV-"._raid(5); + $t->username = $c['username']; + $t->plan_name = $p['name_plan']; + $t->price = $p['price']; + $t->recharged_on = $date_only; + $t->expiration = $date_exp; + $t->time = $time; + $t->method = "admin"; + $t->routers = $server; + $t->type = "PPPOE"; + $t->save(); + + }else{ + try { + $client = new RouterOS\Client($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + } catch (Exception $e) { + die('Unable to connect to the router.'); + } + $addRequest = new RouterOS\Request('/ppp/secret/add'); + $client->sendSync($addRequest + ->setArgument('name', $c['username']) + ->setArgument('service', 'pppoe') + ->setArgument('profile', $p['name_plan']) + ->setArgument('password', $c['password']) + ); + + $d = ORM::for_table('tbl_user_recharges')->create(); + $d->customer_id = $id_customer; + $d->username = $c['username']; + $d->plan_id = $plan; + $d->namebp = $p['name_plan']; + $d->recharged_on = $date_only; + $d->expiration = $date_exp; + $d->time = $time; + $d->status = "on"; + $d->method = "admin"; + $d->routers = $server; + $d->type = "PPPOE"; + $d->save(); + + // insert table transactions + $t = ORM::for_table('tbl_transactions')->create(); + $t->invoice = "INV-"._raid(5); + $t->username = $c['username']; + $t->plan_name = $p['name_plan']; + $t->price = $p['price']; + $t->recharged_on = $date_only; + $t->expiration = $date_exp; + $t->time = $time; + $t->method = "admin"; + $t->routers = $server; + $t->type = "PPPOE"; + $t->save(); + } + } + $in = ORM::for_table('tbl_transactions')->where('username',$c['username'])->order_by_desc('id')->find_one(); + $ui->assign('in',$in); + + $ui->assign('date',$date_now); + $ui->display('invoice.tpl'); + + }else{ + r2(U . 'prepaid/recharge', 'e', $msg); + } + break; + + case 'print': + $date_now = date("Y-m-d H:i:s"); + $id = _post('id'); + + $d = ORM::for_table('tbl_transactions')->where('id',$id)->find_one(); + $ui->assign('d',$d); + + $ui->assign('date',$date_now); + $ui->display('invoice-print.tpl'); + break; + break; + + case 'edit': + $id = $routes['2']; + $d = ORM::for_table('tbl_user_recharges')->find_one($id); + if($d){ + $ui->assign('d',$d); + $p = ORM::for_table('tbl_plans')->find_many(); + $ui->assign('p',$p); + + $ui->display('prepaid-edit.tpl'); + }else{ + r2(U . 'services/list', 'e', $_L['Account_Not_Found']); + } + break; + + case 'delete': + $id = $routes['2']; + + $d = ORM::for_table('tbl_user_recharges')->find_one($id); + $mikrotik = Router::_info($d['routers']); + if($d){ + if($d['type'] == 'Hotspot'){ + try { + $client = new RouterOS\Client($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + } catch (Exception $e) { + die('Unable to connect to the router.'); + } + $printRequest = new RouterOS\Request( + '/ip hotspot user print .proplist=name', + RouterOS\Query::where('name', $d['username']) + ); + $userName = $client->sendSync($printRequest)->getProperty('name'); + $removeRequest = new RouterOS\Request('/ip/hotspot/user/remove'); + $client($removeRequest + ->setArgument('numbers', $userName) + ); + + $d->delete(); + }else{ + try { + $client = new RouterOS\Client($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + } catch (Exception $e) { + die('Unable to connect to the router.'); + } + $printRequest = new RouterOS\Request( + '/ppp secret print .proplist=name', + RouterOS\Query::where('name', $d['username']) + ); + $userName = $client->sendSync($printRequest)->getProperty('name'); + + $removeRequest = new RouterOS\Request('/ppp/secret/remove'); + $client($removeRequest + ->setArgument('numbers', $userName) + ); + $d->delete(); + } + r2(U . 'prepaid/list', 's', $_L['Delete_Successfully']); + } + break; + + case 'edit-post': + $username = _post('username'); + $id_plan = _post('id_plan'); + $recharged_on = _post('recharged_on'); + $expiration = _post('expiration'); + + $id = _post('id'); + $d = ORM::for_table('tbl_user_recharges')->find_one($id); + if($d){ + + }else{ + $msg .= $_L['Data_Not_Found']. '
'; + } + + if($msg == ''){ + $d->username = $username; + $d->plan_id = $id_plan; + $d->recharged_on = $recharged_on; + $d->expiration = $expiration; + $d->save(); + + r2(U . 'prepaid/list', 's', $_L['Updated_Successfully']); + }else{ + r2(U . 'prepaid/edit/'.$id, 'e', $msg); + } + break; + + case 'voucher': + $ui->assign('xfooter', ''); + + $code = _post('code'); + if ($code != ''){ + $paginator = Paginator::bootstrap('tbl_voucher','code','%'.$code.'%'); + $d = ORM::for_table('tbl_plans')->join('tbl_voucher', array('tbl_plans.id', '=', 'tbl_voucher.id_plan'))->where_like('tbl_plans.code','%'.$code.'%')->offset($paginator['startpoint'])->limit($paginator['limit'])->find_many(); + }else{ + $paginator = Paginator::bootstrap('tbl_voucher'); + $d = ORM::for_table('tbl_plans')->join('tbl_voucher', array('tbl_plans.id', '=', 'tbl_voucher.id_plan'))->offset($paginator['startpoint'])->limit($paginator['limit'])->find_many(); + } + + $ui->assign('d',$d); + $ui->assign('paginator',$paginator); + $ui->display('voucher.tpl'); + break; + + case 'add-voucher': + + $c = ORM::for_table('tbl_customers')->find_many(); + $ui->assign('c',$c); + $p = ORM::for_table('tbl_plans')->find_many(); + $ui->assign('p',$p); + $r = ORM::for_table('tbl_routers')->find_many(); + $ui->assign('r',$r); + + $ui->display('voucher-add.tpl'); + break; + + case 'voucher-post': + $type = _post('type'); + $plan = _post('plan'); + $server = _post('server'); + $numbervoucher = _post('numbervoucher'); + $lengthcode = _post('lengthcode'); + + $msg = ''; + if ($type == '' OR $plan == '' OR $server == '' OR $numbervoucher == '' OR $lengthcode == ''){ + $msg .= $_L['All_field_is_required']. '
'; + } + if(Validator::UnsignedNumber($numbervoucher) == false){ + $msg .= 'The Number of Vouchers must be a number'. '
'; + } + if(Validator::UnsignedNumber($lengthcode) == false){ + $msg .= 'The Length Code must be a number'. '
'; + } + if($msg == ''){ + for ($i=0; $i < $numbervoucher; $i++){ + $code = strtoupper(substr(md5(time().rand(10000,99999)),0,$lengthcode)); + + $d = ORM::for_table('tbl_voucher')->create(); + $d->type = $type; + $d->routers = $server; + $d->id_plan = $plan; + $d->code = $code; + $d->user = '0'; + $d->status = '0'; + $d->save(); + } + + r2(U . 'prepaid/voucher', 's', $_L['Voucher_Successfully']); + }else{ + r2(U . 'prepaid/add-voucher/'.$id, 'e', $msg); + } + break; + + case 'voucher-delete': + $id = $routes['2']; + + $d = ORM::for_table('tbl_voucher')->find_one($id); + if($d){ + $d->delete(); + r2(U . 'prepaid/voucher', 's', $_L['Delete_Successfully']); + } + break; + + case 'refill': + $ui->assign('xfooter', ''); + + $c = ORM::for_table('tbl_customers')->find_many(); + $ui->assign('c',$c); + + $ui->display('refill.tpl'); + + break; + + case 'refill-post': + $user = _post('id_customer'); + $code = _post('code'); + + $v1 = ORM::for_table('tbl_voucher')->where('code',$code)->where('status',0)->find_one(); + + $c = ORM::for_table('tbl_customers')->find_one($user); + $p = ORM::for_table('tbl_plans')->find_one($v1['id_plan']); + $b = ORM::for_table('tbl_user_recharges')->where('customer_id',$user)->find_one(); + + $date_now = date("Y-m-d H:i:s"); + $date_only = date("Y-m-d"); + $time = date("H:i:s"); + + $mikrotik = Router::_info($v1['routers']); + $date_exp = date("Y-m-d", mktime(0,0,0,date("m"),date("d") + $p['validity'],date("Y"))); + + if ($v1){ + if($v1['type'] == 'Hotspot'){ + if($b){ + try { + $client = new RouterOS\Client($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + } catch (Exception $e) { + die('Unable to connect to the router.'); + } + $printRequest = new RouterOS\Request( + '/ip hotspot user print .proplist=name', + RouterOS\Query::where('name', $c['username']) + ); + $userName = $client->sendSync($printRequest)->getProperty('name'); + $removeRequest = new RouterOS\Request('/ip/hotspot/user/remove'); + $client($removeRequest + ->setArgument('numbers', $userName) + ); + $addRequest = new RouterOS\Request('/ip/hotspot/user/add'); + $client->sendSync($addRequest + ->setArgument('name', $c['username']) + ->setArgument('profile', $p['name_plan']) + ->setArgument('password', $c['password']) + ); + + $b->customer_id = $user; + $b->username = $c['username']; + $b->plan_id = $v1['id_plan']; + $b->namebp = $p['name_plan']; + $b->recharged_on = $date_only; + $b->expiration = $date_exp; + $b->time = $time; + $b->status = "on"; + $b->method = "voucher"; + $b->routers = $v1['routers']; + $b->type = "Hotspot"; + $b->save(); + + // insert table transactions + $t = ORM::for_table('tbl_transactions')->create(); + $t->invoice = "INV-"._raid(5); + $t->username = $c['username']; + $t->plan_name = $p['name_plan']; + $t->price = $p['price']; + $t->recharged_on = $date_only; + $t->expiration = $date_exp; + $t->time = $time; + $t->method = "voucher"; + $t->routers = $v1['routers']; + $t->type = "Hotspot"; + $t->save(); + + }else{ + try { + $client = new RouterOS\Client($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + } catch (Exception $e) { + die('Unable to connect to the router.'); + } + $addRequest = new RouterOS\Request('/ip/hotspot/user/add'); + $client->sendSync($addRequest + ->setArgument('name', $c['username']) + ->setArgument('profile', $p['name_plan']) + ->setArgument('password', $c['password']) + ); + + $d = ORM::for_table('tbl_user_recharges')->create(); + $d->customer_id = $user; + $d->username = $c['username']; + $d->plan_id = $v1['id_plan']; + $d->namebp = $p['name_plan']; + $d->recharged_on = $date_only; + $d->expiration = $date_exp; + $d->time = $time; + $d->status = "on"; + $d->method = "voucher"; + $d->routers = $v1['routers']; + $d->type = "Hotspot"; + $d->save(); + + // insert table transactions + $t = ORM::for_table('tbl_transactions')->create(); + $t->invoice = "INV-"._raid(5); + $t->username = $c['username']; + $t->plan_name = $p['name_plan']; + $t->price = $p['price']; + $t->recharged_on = $date_only; + $t->expiration = $date_exp; + $t->time = $time; + $t->method = "voucher"; + $t->routers = $v1['routers']; + $t->type = "Hotspot"; + $t->save(); + + } + + $v1->status = "1"; + $v1->user = $c['username']; + $v1->save(); + + }else{ + if($b){ + try { + $client = new RouterOS\Client($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + } catch (Exception $e) { + die('Unable to connect to the router.'); + } + $printRequest = new RouterOS\Request( + '/ppp secret print .proplist=name', + RouterOS\Query::where('name', $c['username']) + ); + $userName = $client->sendSync($printRequest)->getProperty('name'); + + $removeRequest = new RouterOS\Request('/ppp/secret/remove'); + $client($removeRequest + ->setArgument('numbers', $userName) + ); + + $addRequest = new RouterOS\Request('/ppp/secret/add'); + $client->sendSync($addRequest + ->setArgument('name', $c['username']) + ->setArgument('service', 'pppoe') + ->setArgument('profile', $p['name_plan']) + ->setArgument('password', $c['password']) + ); + + $b->customer_id = $user; + $b->username = $c['username']; + $b->plan_id = $v1['id_plan']; + $b->namebp = $p['name_plan']; + $b->recharged_on = $date_only; + $b->expiration = $date_exp; + $b->time = $time; + $b->status = "on"; + $b->method = "voucher"; + $b->routers = $v1['routers']; + $b->type = "PPPOE"; + $b->save(); + + // insert table transactions + $t = ORM::for_table('tbl_transactions')->create(); + $t->invoice = "INV-"._raid(5); + $t->username = $c['username']; + $t->plan_name = $p['name_plan']; + $t->price = $p['price']; + $t->recharged_on = $date_only; + $t->expiration = $date_exp; + $t->time = $time; + $t->method = "voucher"; + $t->routers = $v1['routers']; + $t->type = "PPPOE"; + $t->save(); + + }else{ + try { + $client = new RouterOS\Client($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + } catch (Exception $e) { + die('Unable to connect to the router.'); + } + $addRequest = new RouterOS\Request('/ppp/secret/add'); + $client->sendSync($addRequest + ->setArgument('name', $c['username']) + ->setArgument('service', 'pppoe') + ->setArgument('profile', $p['name_plan']) + ->setArgument('password', $c['password']) + ); + + $d = ORM::for_table('tbl_user_recharges')->create(); + $d->customer_id = $user; + $d->username = $c['username']; + $d->plan_id = $v1['id_plan']; + $d->namebp = $p['name_plan']; + $d->recharged_on = $date_only; + $d->expiration = $date_exp; + $d->time = $time; + $d->status = "on"; + $d->method = "voucher"; + $d->routers = $v1['routers']; + $d->type = "PPPOE"; + $d->save(); + + // insert table transactions + $t = ORM::for_table('tbl_transactions')->create(); + $t->invoice = "INV-"._raid(5); + $t->username = $c['username']; + $t->plan_name = $p['name_plan']; + $t->price = $p['price']; + $t->recharged_on = $date_only; + $t->expiration = $date_exp; + $t->time = $time; + $t->method = "voucher"; + $t->routers = $v1['routers']; + $t->type = "PPPOE"; + $t->save(); + } + + $v1->status = "1"; + $v1->user = $c['username']; + $v1->save(); + } + $in = ORM::for_table('tbl_transactions')->where('username',$c['username'])->order_by_desc('id')->find_one(); + $ui->assign('in',$in); + + $ui->assign('date',$date_now); + $ui->display('invoice.tpl'); + }else{ + r2(U . 'prepaid/refill', 'e', $_L['Voucher_Not_Valid']); + } + break; + + default: + echo 'action not defined'; +} \ No newline at end of file diff --git a/system/controllers/reports.php b/system/controllers/reports.php new file mode 100644 index 0000000..03cfb11 --- /dev/null +++ b/system/controllers/reports.php @@ -0,0 +1,88 @@ + +* @version 5.0 +* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing +* @license GNU General Public License version 2 or later; see LICENSE.txt +* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4 +**/ +_admin(); +$ui->assign('_title', $_L['Reports'].' - '. $config['CompanyName']); +$ui->assign('_system_menu', 'reports'); + +$action = $routes['1']; +$admin = Admin::_info(); +$ui->assign('_admin', $admin); + +if($admin['user_type'] != 'Admin' AND $admin['user_type'] != 'Sales'){ + r2(U."dashboard",'e',$_L['Do_Not_Access']); +} + +$mdate = date('Y-m-d'); +$mtime = date('H:i:s'); +$tdate = date('Y-m-d', strtotime('today - 30 days')); +$firs_day_month = date('Y-m-01'); +$this_week_start = date('Y-m-d', strtotime('previous sunday')); +$before_30_days = date('Y-m-d', strtotime('today - 30 days')); +$month_n = date('n'); + +switch ($action) { + case 'daily-report': + $paginator = Paginator::bootstrap('tbl_transactions','recharged_on',$mdate); + $d = ORM::for_table('tbl_transactions')->where('recharged_on',$mdate)->offset($paginator['startpoint'])->limit($paginator['limit'])->order_by_desc('id')->find_many(); + $dr = ORM::for_table('tbl_transactions')->where('recharged_on',$mdate)->sum('price'); + + $ui->assign('d',$d); + $ui->assign('dr',$dr); + $ui->assign('mdate',$mdate); + $ui->assign('mtime',$mtime); + $ui->assign('paginator',$paginator); + + $ui->display('reports-daily.tpl'); + break; + + case 'by-period': + $ui->assign('mdate',$mdate); + $ui->assign('mtime',$mtime); + $ui->assign('tdate', $tdate); + + $ui->display('reports-period.tpl'); + break; + + case 'period-view': + $fdate = _post('fdate'); + $tdate = _post('tdate'); + $stype = _post('stype'); + + $d = ORM::for_table('tbl_transactions'); + if ($stype != ''){ + $d->where('type', $stype); + } + + $d->where_gte('recharged_on', $fdate); + $d->where_lte('recharged_on', $tdate); + $d->order_by_desc('id'); + $x = $d->find_many(); + + $dr = ORM::for_table('tbl_transactions'); + if ($stype != ''){ + $dr->where('type', $stype); + } + + $dr->where_gte('recharged_on', $fdate); + $dr->where_lte('recharged_on', $tdate); + $xy = $dr->sum('price'); + + $ui->assign('d',$x); + $ui->assign('dr',$xy); + $ui->assign('fdate',$fdate); + $ui->assign('tdate',$tdate); + $ui->assign('stype',$stype); + + $ui->display('reports-period-view.tpl'); + break; + + default: + echo 'action not defined'; +} \ No newline at end of file diff --git a/system/controllers/routers.php b/system/controllers/routers.php new file mode 100644 index 0000000..bc89010 --- /dev/null +++ b/system/controllers/routers.php @@ -0,0 +1,148 @@ + +* @version 5.0 +* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing +* @license GNU General Public License version 2 or later; see LICENSE.txt +* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4 +**/ +_admin(); +$ui->assign('_title', $_L['Network'].' - '. $config['CompanyName']); +$ui->assign('_system_menu', 'network'); + +$action = $routes['1']; +$admin = Admin::_info(); +$ui->assign('_admin', $admin); + +if($admin['user_type'] != 'Admin'){ + r2(U."dashboard",'e',$_L['Do_Not_Access']); +} + +switch ($action) { + case 'list': + $ui->assign('xfooter', ''); + + $name = _post('name'); + if ($name != ''){ + $paginator = Paginator::bootstrap('tbl_routers','name','%'.$name.'%'); + $d = ORM::for_table('tbl_routers')->where_like('name','%'.$name.'%')->offset($paginator['startpoint'])->limit($paginator['limit'])->order_by_desc('id')->find_many(); + }else{ + $paginator = Paginator::bootstrap('tbl_routers'); + $d = ORM::for_table('tbl_routers')->offset($paginator['startpoint'])->limit($paginator['limit'])->order_by_desc('id')->find_many(); + } + + $ui->assign('d',$d); + $ui->assign('paginator',$paginator); + $ui->display('routers.tpl'); + break; + + case 'add': + $d = ORM::for_table('tbl_routers')->find_many(); + $ui->assign('d',$d); + $ui->display('routers-add.tpl'); + break; + + case 'edit': + $id = $routes['2']; + $d = ORM::for_table('tbl_routers')->find_one($id); + if($d){ + $ui->assign('d',$d); + $ui->display('routers-edit.tpl'); + }else{ + r2(U . 'routers/list', 'e', $_L['Account_Not_Found']); + } + break; + + case 'delete': + $id = $routes['2']; + + $d = ORM::for_table('tbl_routers')->find_one($id); + if($d){ + $d->delete(); + r2(U . 'routers/list', 's', $_L['Delete_Successfully']); + } + break; + + case 'add-post': + $name = _post('name'); + $ip_address = _post('ip_address'); + $username = _post('username'); + $password = _post('password'); + $description = _post('description'); + + $msg = ''; + if(Validator::Length($name,30,4) == false){ + $msg .= 'Name should be between 5 to 30 characters'. '
'; + } + if ($ip_address == '' OR $username == ''){ + $msg .= $_L['All_field_is_required']. '
'; + } + + $d = ORM::for_table('tbl_routers')->where('ip_address',$ip_address)->find_one(); + if($d){ + $msg .= $_L['Router_already_exist']. '
'; + } + + if($msg == ''){ + $d = ORM::for_table('tbl_routers')->create(); + $d->name = $name; + $d->ip_address = $ip_address; + $d->username = $username; + $d->password = $password; + $d->description = $description; + $d->save(); + + r2(U . 'routers/list', 's', $_L['Created_Successfully']); + }else{ + r2(U . 'routers/add', 'e', $msg); + } + break; + + + case 'edit-post': + $name = _post('name'); + $ip_address = _post('ip_address'); + $username = _post('username'); + $password = _post('password'); + $description = _post('description'); + + $msg = ''; + if(Validator::Length($name,30,4) == false){ + $msg .= 'Name should be between 5 to 30 characters'. '
'; + } + if ($ip_address == '' OR $username == ''){ + $msg .= $_L['All_field_is_required']. '
'; + } + + $id = _post('id'); + $d = ORM::for_table('tbl_routers')->find_one($id); + if($d){ + + }else{ + $msg .= $_L['Data_Not_Found']. '
'; + } + + if($d['name'] != $name){ + $c = ORM::for_table('tbl_routers')->where('ip_address',$ip_address)->find_one(); + if($c){ + $msg .= $_L['Router_already_exist']. '
'; + } + } + + if($msg == ''){ + $d->name = $name; + $d->ip_address = $ip_address; + $d->username = $username; + $d->password = $password; + $d->description = $description; + $d->save(); + r2(U . 'routers/list', 's', $_L['Updated_Successfully']); + }else{ + r2(U . 'routers/edit/'.$id, 'e', $msg); + } + break; + + default: + echo 'action not defined'; +} \ No newline at end of file diff --git a/system/controllers/services.php b/system/controllers/services.php new file mode 100644 index 0000000..a76f8ca --- /dev/null +++ b/system/controllers/services.php @@ -0,0 +1,455 @@ + +* @version 5.0 +* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing +* @license GNU General Public License version 2 or later; see LICENSE.txt +* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4 +**/ +_admin(); +$ui->assign('_title', $_L['Hotspot_Plans'].' - '. $config['CompanyName']); +$ui->assign('_system_menu', 'services'); + +$action = $routes['1']; +$admin = Admin::_info(); +$ui->assign('_admin', $admin); + +if($admin['user_type'] != 'Admin' AND $admin['user_type'] != 'Sales'){ + r2(U."dashboard",'e',$_L['Do_Not_Access']); +} + +use PEAR2\Net\RouterOS; +require_once 'system/autoload/PEAR2/Autoload.php'; + +switch ($action) { + case 'hotspot': + $ui->assign('xfooter', ''); + + $name = _post('name'); + if ($name != ''){ + $paginator = Paginator::bootstrap('tbl_plans','name_plan','%'.$name.'%','type','Hotspot'); + $d = ORM::for_table('tbl_bandwidth')->join('tbl_plans', array('tbl_bandwidth.id', '=', 'tbl_plans.id_bw'))->where('tbl_plans.type','Hotspot')->where_like('tbl_plans.name_plan','%'.$name.'%')->offset($paginator['startpoint'])->limit($paginator['limit'])->find_many(); + }else{ + $paginator = Paginator::bootstrap('tbl_plans','type','Hotspot'); + $d = ORM::for_table('tbl_bandwidth')->join('tbl_plans', array('tbl_bandwidth.id', '=', 'tbl_plans.id_bw'))->where('tbl_plans.type','Hotspot')->offset($paginator['startpoint'])->limit($paginator['limit'])->find_many(); + } + + $ui->assign('d',$d); + $ui->assign('paginator',$paginator); + $ui->display('hotspot.tpl'); + break; + + case 'add': + $d = ORM::for_table('tbl_bandwidth')->find_many(); + $ui->assign('d',$d); + $r = ORM::for_table('tbl_routers')->find_many(); + $ui->assign('r',$r); + + $ui->display('hotspot-add.tpl'); + break; + + case 'edit': + $id = $routes['2']; + $d = ORM::for_table('tbl_plans')->find_one($id); + if($d){ + $ui->assign('d',$d); + $b = ORM::for_table('tbl_bandwidth')->find_many(); + $ui->assign('b',$b); + $r = ORM::for_table('tbl_routers')->find_many(); + $ui->assign('r',$r); + + $ui->display('hotspot-edit.tpl'); + }else{ + r2(U . 'services/hotspot', 'e', $_L['Account_Not_Found']); + } + break; + + case 'delete': + $id = $routes['2']; + + $d = ORM::for_table('tbl_plans')->find_one($id); + if($d){ + $mikrotik = Router::_info($d['routers']); + try { + $client = new RouterOS\Client($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + } catch (Exception $e) { + die('Unable to connect to the router.'); + } + $printRequest = new RouterOS\Request( + '/ip hotspot user profile print .proplist=name', + RouterOS\Query::where('name', $d['name_plan']) + ); + $profileName = $client->sendSync($printRequest)->getProperty('name'); + + $removeRequest = new RouterOS\Request('/ip/hotspot/user/profile/remove'); + $client($removeRequest + ->setArgument('numbers', $profileName) + ); + + $d->delete(); + + r2(U . 'services/hotspot', 's', $_L['Delete_Successfully']); + } + break; + + case 'add-post': + $name = _post('name'); + $typebp = _post('typebp'); + $limit_type = _post('limit_type'); + $time_limit = _post('time_limit'); + $time_unit = _post('time_unit'); + $data_limit = _post('data_limit'); + $data_unit = _post('data_unit'); + $id_bw = _post('id_bw'); + $price = _post('pricebp'); + $sharedusers = _post('sharedusers'); + $validity = _post('validity'); + $validity_unit = _post('validity_unit'); + $routers = _post('routers'); + + $msg = ''; + if(Validator::UnsignedNumber($validity) == false){ + $msg .= 'The validity must be a number'. '
'; + } + if(Validator::UnsignedNumber($price) == false){ + $msg .= 'The price must be a number'. '
'; + } + if ($name == '' OR $id_bw == '' OR $price == '' OR $validity == '' OR $routers == ''){ + $msg .= $_L['All_field_is_required']. '
'; + } + + $d = ORM::for_table('tbl_plans')->where('name_plan',$name)->where('type','Hotspot')->find_one(); + if($d){ + $msg .= $_L['Plan_already_exist']. '
'; + } + + if($msg == ''){ + $b = ORM::for_table('tbl_bandwidth')->where('id',$id_bw)->find_one(); + if($b['rate_down_unit'] == 'Kbps'){ $unitdown = 'K'; }else{ $unitdown = 'M'; } + if($b['rate_up_unit'] == 'Kbps'){ $unitup = 'K'; }else{ $unitup = 'M'; } + $rate = $b['rate_up'].$unitup."/".$b['rate_down'].$unitdown; + + $mikrotik = Router::_info($routers); + try { + $client = new RouterOS\Client($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + } catch (Exception $e) { + die('Unable to connect to the router.'); + } + $addRequest = new RouterOS\Request('/ip/hotspot/user/profile/add'); + $client->sendSync($addRequest + ->setArgument('name', $name) + ->setArgument('shared-users', $sharedusers) + ->setArgument('rate-limit', $rate) + ); + + $d = ORM::for_table('tbl_plans')->create(); + $d->name_plan = $name; + $d->id_bw = $id_bw; + $d->price = $price; + $d->type = 'Hotspot'; + $d->typebp = $typebp; + $d->limit_type = $limit_type; + $d->time_limit = $time_limit; + $d->time_unit = $time_unit; + $d->data_limit = $data_limit; + $d->data_unit = $data_unit; + $d->validity = $validity; + $d->validity_unit = $validity_unit; + $d->shared_users = $sharedusers; + $d->routers = $routers; + $d->save(); + + r2(U . 'services/hotspot', 's', $_L['Created_Successfully']); + }else{ + r2(U . 'services/add', 'e', $msg); + } + break; + + + case 'edit-post': + $id = _post('id'); + $name = _post('name'); + $id_bw = _post('id_bw'); + $typebp = _post('typebp'); + $price = _post('price'); + $limit_type = _post('limit_type'); + $time_limit = _post('time_limit'); + $time_unit = _post('time_unit'); + $data_limit = _post('data_limit'); + $data_unit = _post('data_unit'); + $sharedusers = _post('sharedusers'); + $validity = _post('validity'); + $validity_unit = _post('validity_unit'); + $routers = _post('routers'); + + $msg = ''; + if(Validator::UnsignedNumber($validity) == false){ + $msg .= 'The validity must be a number'. '
'; + } + if(Validator::UnsignedNumber($price) == false){ + $msg .= 'The price must be a number'. '
'; + } + if ($name == '' OR $id_bw == '' OR $price == '' OR $validity == '' OR $routers == ''){ + $msg .= $_L['All_field_is_required']. '
'; + } + + $d = ORM::for_table('tbl_plans')->where('id',$id)->find_one(); + if($d){ + }else{ + $msg .= $_L['Data_Not_Found']. '
'; + } + + if($msg == ''){ + $b = ORM::for_table('tbl_bandwidth')->where('id',$id_bw)->find_one(); + if($b['rate_down_unit'] == 'Kbps'){ $unitdown = 'K'; }else{ $unitdown = 'M'; } + if($b['rate_up_unit'] == 'Kbps'){ $unitup = 'K'; }else{ $unitup = 'M'; } + $rate = $b['rate_up'].$unitup."/".$b['rate_down'].$unitdown; + + $mikrotik = Router::_info($routers); + try { + $client = new RouterOS\Client($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + } catch (Exception $e) { + die('Unable to connect to the router.'); + } + $printRequest = new RouterOS\Request( + '/ip hotspot user profile print .proplist=name', + RouterOS\Query::where('name', $name) + ); + $profileName = $client->sendSync($printRequest)->getProperty('name'); + + $setRequest = new RouterOS\Request('/ip/hotspot/user/profile/set'); + $client($setRequest + ->setArgument('numbers', $profileName) + ->setArgument('shared-users', $sharedusers) + ->setArgument('rate-limit', $rate) + ); + + $d->name_plan = $name; + $d->id_bw = $id_bw; + $d->price = $price; + $d->typebp = $typebp; + $d->limit_type = $limit_type; + $d->time_limit = $time_limit; + $d->time_unit = $time_unit; + $d->data_limit = $data_limit; + $d->data_unit = $data_unit; + $d->validity = $validity; + $d->validity_unit = $validity_unit; + $d->shared_users = $sharedusers; + $d->routers = $routers; + $d->save(); + + r2(U . 'services/hotspot', 's', $_L['Updated_Successfully']); + }else{ + r2(U . 'services/edit/'.$id, 'e', $msg); + } + break; + + case 'pppoe': + $ui->assign('xfooter', ''); + + $name = _post('name'); + if ($name != ''){ + $paginator = Paginator::bootstrap('tbl_plans','name_plan','%'.$name.'%','type','Hotspot'); + $d = ORM::for_table('tbl_bandwidth')->join('tbl_plans', array('tbl_bandwidth.id', '=', 'tbl_plans.id_bw'))->where('tbl_plans.type','PPPOE')->where_like('tbl_plans.name_plan','%'.$name.'%')->offset($paginator['startpoint'])->limit($paginator['limit'])->find_many(); + }else{ + $paginator = Paginator::bootstrap('tbl_plans','type','Hotspot'); + $d = ORM::for_table('tbl_bandwidth')->join('tbl_plans', array('tbl_bandwidth.id', '=', 'tbl_plans.id_bw'))->where('tbl_plans.type','PPPOE')->offset($paginator['startpoint'])->limit($paginator['limit'])->find_many(); + } + + $ui->assign('d',$d); + $ui->assign('paginator',$paginator); + $ui->display('pppoe.tpl'); + break; + + case 'pppoe-add': + $d = ORM::for_table('tbl_bandwidth')->find_many(); + $ui->assign('d',$d); + $p = ORM::for_table('tbl_pool')->find_many(); + $ui->assign('p',$p); + $r = ORM::for_table('tbl_routers')->find_many(); + $ui->assign('r',$r); + + $ui->display('pppoe-add.tpl'); + break; + + case 'pppoe-edit': + $id = $routes['2']; + $d = ORM::for_table('tbl_plans')->find_one($id); + if($d){ + $ui->assign('d',$d); + $b = ORM::for_table('tbl_bandwidth')->find_many(); + $ui->assign('b',$b); + $p = ORM::for_table('tbl_pool')->find_many(); + $ui->assign('p',$p); + $r = ORM::for_table('tbl_routers')->find_many(); + $ui->assign('r',$r); + + $ui->display('pppoe-edit.tpl'); + }else{ + r2(U . 'services/pppoe', 'e', $_L['Account_Not_Found']); + } + break; + + case 'pppoe-delete': + $id = $routes['2']; + + $d = ORM::for_table('tbl_plans')->find_one($id); + if($d){ + $mikrotik = Router::_info($d['routers']); + try { + $client = new RouterOS\Client($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + } catch (Exception $e) { + die('Unable to connect to the router.'); + } + $printRequest = new RouterOS\Request( + '/ppp profile print .proplist=name', + RouterOS\Query::where('name', $d['name_plan']) + ); + $profileName = $client->sendSync($printRequest)->getProperty('name'); + + $removeRequest = new RouterOS\Request('/ppp/profile/remove'); + $client($removeRequest + ->setArgument('numbers', $profileName) + ); + + $d->delete(); + + r2(U . 'services/pppoe', 's', $_L['Delete_Successfully']); + } + break; + + case 'pppoe-add-post': + $name = _post('name_plan'); + $id_bw = _post('id_bw'); + $price = _post('price'); + $validity = _post('validity'); + $validity_unit = _post('validity_unit'); + $routers = _post('routers'); + $pool = _post('pool_name'); + + $msg = ''; + if(Validator::UnsignedNumber($validity) == false){ + $msg .= 'The validity must be a number'. '
'; + } + if(Validator::UnsignedNumber($price) == false){ + $msg .= 'The price must be a number'. '
'; + } + if ($name == '' OR $id_bw == '' OR $price == '' OR $validity == '' OR $routers == '' OR $pool == ''){ + $msg .= $_L['All_field_is_required']. '
'; + } + + $d = ORM::for_table('tbl_plans')->where('name_plan',$name)->find_one(); + if($d){ + $msg .= $_L['Plan_already_exist']. '
'; + } + + if($msg == ''){ + $b = ORM::for_table('tbl_bandwidth')->where('id',$id_bw)->find_one(); + if($b['rate_down_unit'] == 'Kbps'){ $unitdown = 'K'; }else{ $unitdown = 'M'; } + if($b['rate_up_unit'] == 'Kbps'){ $unitup = 'K'; }else{ $unitup = 'M'; } + $rate = $b['rate_up'].$unitup."/".$b['rate_down'].$unitdown; + + $mikrotik = Router::_info($routers); + try { + $client = new RouterOS\Client($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + } catch (Exception $e) { + die('Unable to connect to the router.'); + } + $addRequest = new RouterOS\Request('/ppp/profile/add'); + $client->sendSync($addRequest + ->setArgument('name', $name) + ->setArgument('local-address', $pool) + ->setArgument('remote-address', $pool) + ->setArgument('rate-limit', $rate) + ); + + $d = ORM::for_table('tbl_plans')->create(); + $d->type = 'PPPOE'; + $d->name_plan = $name; + $d->id_bw = $id_bw; + $d->price = $price; + $d->validity = $validity; + $d->validity_unit = $validity_unit; + $d->routers = $routers; + $d->pool = $pool; + $d->save(); + + r2(U . 'services/pppoe', 's', $_L['Created_Successfully']); + }else{ + r2(U . 'services/pppoe-add', 'e', $msg); + } + break; + + case 'edit-pppoe-post': + $id = _post('id'); + $name = _post('name_plan'); + $id_bw = _post('id_bw'); + $price = _post('price'); + $validity = _post('validity'); + $validity_unit = _post('validity_unit'); + $routers = _post('routers'); + $pool = _post('pool_name'); + + $msg = ''; + if(Validator::UnsignedNumber($validity) == false){ + $msg .= 'The validity must be a number'. '
'; + } + if(Validator::UnsignedNumber($price) == false){ + $msg .= 'The price must be a number'. '
'; + } + if ($name == '' OR $id_bw == '' OR $price == '' OR $validity == '' OR $routers == '' OR $pool == ''){ + $msg .= $_L['All_field_is_required']. '
'; + } + + $d = ORM::for_table('tbl_plans')->where('id',$id)->find_one(); + if($d){ + }else{ + $msg .= $_L['Data_Not_Found']. '
'; + } + + if($msg == ''){ + $b = ORM::for_table('tbl_bandwidth')->where('id',$id_bw)->find_one(); + if($b['rate_down_unit'] == 'Kbps'){ $unitdown = 'K'; }else{ $unitdown = 'M'; } + if($b['rate_up_unit'] == 'Kbps'){ $unitup = 'K'; }else{ $unitup = 'M'; } + $rate = $b['rate_up'].$unitup."/".$b['rate_down'].$unitdown; + + $mikrotik = Router::_info($routers); + try { + $client = new RouterOS\Client($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + } catch (Exception $e) { + die('Unable to connect to the router.'); + } + $printRequest = new RouterOS\Request( + '/ppp profile print .proplist=name', + RouterOS\Query::where('name', $name) + ); + $profileName = $client->sendSync($printRequest)->getProperty('name'); + + $setRequest = new RouterOS\Request('/ppp/profile/set'); + $client($setRequest + ->setArgument('numbers', $profileName) + ->setArgument('local-address', $pool) + ->setArgument('remote-address', $pool) + ->setArgument('rate-limit', $rate) + ); + + $d->name_plan = $name; + $d->id_bw = $id_bw; + $d->price = $price; + $d->validity = $validity; + $d->validity_unit = $validity_unit; + $d->routers = $routers; + $d->pool = $pool; + $d->save(); + + r2(U . 'services/pppoe', 's', $_L['Updated_Successfully']); + }else{ + r2(U . 'services/pppoe-edit/'.$id, 'e', $msg); + } + break; + + default: + echo 'action not defined'; +} \ No newline at end of file diff --git a/system/controllers/settings.php b/system/controllers/settings.php new file mode 100644 index 0000000..8dc093b --- /dev/null +++ b/system/controllers/settings.php @@ -0,0 +1,478 @@ + +* @version 5.0 +* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing +* @license GNU General Public License version 2 or later; see LICENSE.txt +* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4 +**/ +_admin(); +$ui->assign('_title', $_L['Settings'].'- '. $config['CompanyName']); +$ui->assign('_system_menu', 'settings'); + +$action = $routes['1']; +$admin = Admin::_info(); +$ui->assign('_admin', $admin); + +switch ($action) { + case 'app': + if($admin['user_type'] != 'Admin'){ + r2(U."dashboard",'e',$_L['Do_Not_Access']); + } + + $ui->display('app-settings.tpl'); + break; + + case 'localisation': + if($admin['user_type'] != 'Admin'){ + r2(U."dashboard",'e',$_L['Do_Not_Access']); + } + $lan = ORM::for_table('tbl_language')->find_many(); + $ui->assign('lan',$lan); + + $timezonelist = Timezone::timezoneList(); + $ui->assign('tlist',$timezonelist); + $ui->assign('xjq', ' $("#tzone").select2(); '); + $ui->display('app-localisation.tpl'); + break; + + case 'users': + if($admin['user_type'] != 'Admin'){ + r2(U."dashboard",'e',$_L['Do_Not_Access']); + } + + $ui->assign('xfooter', ''); + + $username = _post('username'); + if ($username != ''){ + $paginator = Paginator::bootstrap('tbl_users','username','%'.$username.'%'); + $d = ORM::for_table('tbl_users')->where_like('username','%'.$username.'%')->offset($paginator['startpoint'])->limit($paginator['limit'])->order_by_asc('id')->find_many(); + }else{ + $paginator = Paginator::bootstrap('tbl_users'); + $d = ORM::for_table('tbl_users')->offset($paginator['startpoint'])->limit($paginator['limit'])->order_by_asc('id')->find_many(); + } + + $ui->assign('d',$d); + $ui->assign('paginator',$paginator); + $ui->display('users.tpl'); + break; + + case 'users-add': + if($admin['user_type'] != 'Admin'){ + r2(U."dashboard",'e',$_L['Do_Not_Access']); + } + + $ui->display('users-add.tpl'); + break; + + case 'users-edit': + if($admin['user_type'] != 'Admin'){ + r2(U."dashboard",'e',$_L['Do_Not_Access']); + } + + $id = $routes['2']; + $d = ORM::for_table('tbl_users')->find_one($id); + if($d){ + $ui->assign('d',$d); + $ui->display('users-edit.tpl'); + }else{ + r2(U . 'settings/users', 'e', $_L['Account_Not_Found']); + } + break; + + case 'users-delete': + if($admin['user_type'] != 'Admin'){ + r2(U."dashboard",'e',$_L['Do_Not_Access']); + } + + $id = $routes['2']; + if(($admin['id']) == $id){ + r2(U . 'settings/users', 'e', 'Sorry You can\'t delete yourself'); + } + $d = ORM::for_table('tbl_users')->find_one($id); + if($d){ + $d->delete(); + r2(U . 'settings/users', 's', $_L['User_Delete_Ok']); + }else{ + r2(U . 'settings/users', 'e', $_L['Account_Not_Found']); + } + break; + + case 'users-post': + $username = _post('username'); + $fullname = _post('fullname'); + $password = _post('password'); + $cpassword = _post('cpassword'); + $user_type = _post('user_type'); + $msg = ''; + if(Validator::Length($username,16,2) == false){ + $msg .= 'Username should be between 3 to 15 characters'. '
'; + } + if(Validator::Length($fullname,26,2) == false){ + $msg .= 'Full Name should be between 3 to 25 characters'. '
'; + } + if(!Validator::Length($password,15,5)){ + $msg .= 'Password should be between 6 to 15 characters'. '
'; + } + if($password != $cpassword){ + $msg .= 'Passwords does not match'. '
'; + } + + $d = ORM::for_table('tbl_users')->where('username',$username)->find_one(); + if($d){ + $msg .= $_L['account_already_exist']. '
'; + } + $date_now = date("Y-m-d H:i:s"); + if($msg == ''){ + $password = Password::_crypt($password); + $d = ORM::for_table('tbl_users')->create(); + $d->username = $username; + $d->fullname = $fullname; + $d->password = $password; + $d->user_type = $user_type; + $d->status = 'Active'; + $d->creationdate = $date_now; + + $d->save(); + + _log('['.$admin['username'].']: '.$_L['account_created_successfully'],'Admin',$admin['id']); + r2(U . 'settings/users', 's', $_L['account_created_successfully']); + }else{ + r2(U . 'settings/users-add', 'e', $msg); + } + break; + + case 'users-edit-post': + $username = _post('username'); + $fullname = _post('fullname'); + $password = _post('password'); + $cpassword = _post('cpassword'); + + $msg = ''; + if(Validator::Length($username,16,2) == false){ + $msg .= 'Username should be between 3 to 15 characters'. '
'; + } + if(Validator::Length($fullname,26,2) == false){ + $msg .= 'Full Name should be between 3 to 25 characters'. '
'; + } + if($password != ''){ + if(!Validator::Length($password,15,5)){ + $msg .= 'Password should be between 6 to 15 characters'. '
'; + } + if($password != $cpassword){ + $msg .= 'Passwords does not match'. '
'; + } + } + + $id = _post('id'); + $d = ORM::for_table('tbl_users')->find_one($id); + if($d){ + }else{ + $msg .= $_L['Data_Not_Found']. '
'; + } + + if($d['username'] != $username){ + $c = ORM::for_table('tbl_users')->where('username',$username)->find_one(); + if($c){ + $msg .= $_L['account_already_exist']. '
'; + } + } + + if($msg == ''){ + $d->username = $username; + if($password != ''){ + $password = Password::_crypt($password); + $d->password = $password; + } + + $d->fullname = $fullname; + if(($admin['id']) != $id){ + $user_type = _post('user_type'); + $d->user_type = $user_type; + } + + $d->save(); + + _log('['.$admin['username'].']: '.$_L['User_Updated_Successfully'],'Admin',$admin['id']); + r2(U . 'settings/users', 's', 'User Updated Successfully'); + }else{ + r2(U . 'settings/users-edit/'.$id, 'e', $msg); + } + break; + + case 'app-post': + $company = _post('company'); + $theme = _post('theme'); + $address = _post('address'); + if($company == '' OR $theme == '' OR $address == ''){ + r2(U.'settings/app','e',$_L['All_field_is_required']); + }else{ + $d = ORM::for_table('tbl_appconfig')->where('setting','CompanyName')->find_one(); + $d->value = $company; + $d->save(); + + $d = ORM::for_table('tbl_appconfig')->where('setting','address')->find_one(); + $d->value = $address; + $d->save(); + + $phone = _post('phone'); + $d = ORM::for_table('tbl_appconfig')->where('setting','phone')->find_one(); + $d->value = $phone; + $d->save(); + + $d = ORM::for_table('tbl_appconfig')->where('setting','theme')->find_one(); + $d->value = $theme; + $d->save(); + + $note = _post('note'); + $d = ORM::for_table('tbl_appconfig')->where('setting','note')->find_one(); + $d->value = $note; + $d->save(); + + _log('['.$admin['username'].']: '.$_L['Settings_Saved_Successfully'],'Admin',$admin['id']); + + r2(U.'settings/app','s',$_L['Settings_Saved_Successfully']); + } + break; + + case 'localisation-post': + $tzone = _post('tzone'); + $date_format = _post('date_format'); + $lan = _post('lan'); + if($tzone == '' OR $date_format == '' OR $lan == ''){ + r2(U.'settings/app','e',$_L['All_field_is_required']); + }else{ + $d = ORM::for_table('tbl_appconfig')->where('setting','timezone')->find_one(); + $d->value = $tzone; + $d->save(); + + $d = ORM::for_table('tbl_appconfig')->where('setting','date_format')->find_one(); + $d->value = $date_format; + $d->save(); + + $dec_point = $_POST['dec_point']; + if(strlen($dec_point) == '1'){ + $d = ORM::for_table('tbl_appconfig')->where('setting','dec_point')->find_one(); + $d->value = $dec_point; + $d->save(); + } + + $thousands_sep = $_POST['thousands_sep']; + if(strlen($thousands_sep) == '1'){ + $d = ORM::for_table('tbl_appconfig')->where('setting','thousands_sep')->find_one(); + $d->value = $thousands_sep; + $d->save(); + } + + $currency_code = $_POST['currency_code']; + $d = ORM::for_table('tbl_appconfig')->where('setting','currency_code')->find_one(); + $d->value = $currency_code; + $d->save(); + + $d = ORM::for_table('tbl_appconfig')->where('setting','language')->find_one(); + $d->value = $lan; + $d->save(); + + _log('['.$admin['username'].']: '.$_L['Settings_Saved_Successfully'],'Admin',$admin['id']); + r2(U.'settings/localisation','s',$_L['Settings_Saved_Successfully']); + } + break; + + case 'change-password': + if($admin['user_type'] != 'Admin' AND $admin['user_type'] != 'Sales'){ + r2(U."dashboard",'e',$_L['Do_Not_Access']); + } + + $ui->display('change-password.tpl'); + break; + + case 'change-password-post': + $password = _post('password'); + if($password != ''){ + $d = ORM::for_table('tbl_users')->where('username',$admin['username'])->find_one(); + if($d){ + $d_pass = $d['password']; + if(Password::_verify($password,$d_pass) == true){ + $npass = _post('npass'); + $cnpass = _post('cnpass'); + if(!Validator::Length($npass,15,5)){ + r2(U.'settings/change-password','e','New Password must be 6 to 14 character'); + } + if($npass != $cnpass){ + r2(U.'settings/change-password','e','Both Password should be same'); + } + + $npass = Password::_crypt($npass); + $d->password = $npass; + $d->save(); + + _msglog('s',$_L['Password_Changed_Successfully']); + _log('['.$admin['username'].']: Password changed successfully','Admin',$admin['id']); + + r2(U.'admin'); + }else{ + r2(U.'settings/change-password','e',$_L['Incorrect_Current_Password']); + } + }else{ + r2(U.'settings/change-password','e',$_L['Incorrect_Current_Password']); + } + }else{ + r2(U.'settings/change-password','e',$_L['Incorrect_Current_Password']); + } + break; + + + case 'dbstatus': + if($admin['user_type'] != 'Admin'){ + r2(U."dashboard",'e',$_L['Do_Not_Access']); + } + + $dbc = new mysqli($db_host,$db_user ,$db_password,$db_name); + if ($result = $dbc->query('SHOW TABLE STATUS')) { + $size = 0; + $decimals = 2; + $tables = array(); + while($row = $result->fetch_array()){ + $size += $row["Data_length"] + $row["Index_length"]; + $total_size = ($row[ "Data_length" ] + $row[ "Index_length" ]) / 1024; + $tables[$row['Name']]['size'] = number_format($total_size,'0'); + $tables[$row['Name']]['rows'] = $row[ "Rows" ]; + $tables[$row['Name']]['name'] = $row[ "Name" ]; + } + $mbytes = number_format($size/(1024*1024),$decimals,$config['dec_point'],$config['thousands_sep']); + + $ui->assign('tables',$tables); + $ui->assign('dbsize',$mbytes); + $ui->display('dbstatus.tpl'); + } + break; + + case 'dbbackup': + if($admin['user_type'] != 'Admin'){ + r2(U."dashboard",'e',$_L['Do_Not_Access']); + } + + try { + $mysqli = new mysqli($db_host,$db_user ,$db_password,$db_name); + if ($mysqli->connect_errno) { + throw new Exception("Failed to connect to MySQL: " . $mysqli->connect_error); + } + + header('Pragma: public'); + header('Expires: 0'); + header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); + header('Content-Type: application/force-download'); + header('Content-Type: application/octet-stream'); + header('Content-Type: application/download'); + header('Content-Disposition: attachment;filename="backup_'.date('Y-m-d_h_i_s') . '.sql"'); + header('Content-Transfer-Encoding: binary'); + + ob_start(); + $f_output = fopen("php://output", 'w'); + + print("-- pjl SQL Dump\n"); + print("-- Server version:".$mysqli->server_info."\n"); + print("-- Generated: ".date('Y-m-d h:i:s')."\n"); + print('-- Current PHP version: '.phpversion()."\n"); + print('-- Host: '.$db_host."\n"); + print('-- Database:'.$db_name."\n"); + + $aTables = array(); + $strSQL = 'SHOW TABLES'; + if (!$res_tables = $mysqli->query($strSQL)) + throw new Exception("MySQL Error: " . $mysqli->error . 'SQL: '.$strSQL); + + while($row = $res_tables->fetch_array()) { + $aTables[] = $row[0]; + } + + $res_tables->free(); + + foreach($aTables as $table) + { + print("-- --------------------------------------------------------\n"); + print("-- Structure for '". $table."'\n"); + print("--\n\n"); + + $strSQL = 'SHOW CREATE TABLE '.$table; + if (!$res_create = $mysqli->query($strSQL)) + throw new Exception("MySQL Error: " . $mysqli->error . 'SQL: '.$strSQL); + $row_create = $res_create->fetch_assoc(); + + print("\n".$row_create['Create Table'].";\n"); + print("-- --------------------------------------------------------\n"); + print('-- Dump Data for `'. $table."`\n"); + print("--\n\n"); + $res_create->free(); + + $strSQL = 'SELECT * FROM '.$table; + if (!$res_select = $mysqli->query($strSQL)) + throw new Exception("MySQL Error: " . $mysqli->error . 'SQL: '.$strSQL); + + $fields_info = $res_select->fetch_fields(); + + while ($values = $res_select->fetch_assoc()) { + $strFields = ''; + $strValues = ''; + foreach ($fields_info as $field) { + if ($strFields != '') $strFields .= ','; + $strFields .= "`".$field->name."`"; + + if ($strValues != '') $strValues .= ','; + $strValues .= '"'.preg_replace('/[^(\x20-\x7F)\x0A]*/','',$values[$field->name].'"'); + } + print("INSERT INTO ".$table." (".$strFields.") VALUES (".$strValues.");\n"); + } + print("\n\n\n"); + $res_select->free(); + } + _log('['.$admin['username'].']: '.$_L['Download_Database_Backup'],'Admin',$admin['id']); + + } catch (Exception $e) { + print($e->getMessage()); + } + + fclose($f_output); + print(ob_get_clean()); + $mysqli->close(); + + break; + + case 'language': + if($admin['user_type'] != 'Admin'){ + r2(U."dashboard",'e',$_L['Do_Not_Access']); + } + + $ui->display('language-add.tpl'); + break; + + case 'lang-post': + $name = _post('name'); + $folder = _post('folder'); + $translator = _post('translator'); + + if ($name == '' OR $folder == ''){ + $msg .= $_L['All_field_is_required']. '
'; + } + + $d = ORM::for_table('tbl_language')->where('name',$name)->find_one(); + if($d){ + $msg .= $_L['Lang_already_exist']. '
'; + } + if($msg == ''){ + $b = ORM::for_table('tbl_language')->create(); + $b->name = $name; + $b->folder = $folder; + $b->author = $translator; + $b->save(); + + r2(U . 'settings/localisation', 's', $_L['Created_Successfully']); + }else{ + r2(U . 'settings/language', 'e', $msg); + } + break; + + default: + echo 'action not defined'; +} \ No newline at end of file diff --git a/system/controllers/voucher.php b/system/controllers/voucher.php new file mode 100644 index 0000000..19e68c6 --- /dev/null +++ b/system/controllers/voucher.php @@ -0,0 +1,258 @@ + +* @version 5.0 +* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing +* @license GNU General Public License version 2 or later; see LICENSE.txt +* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4 +**/ +_auth(); +$ui->assign('_title', $_L['Voucher'].'- '. $config['CompanyName']); +$ui->assign('_system_menu', 'voucher'); + +$action = $routes['1']; +$user = User::_info(); +$ui->assign('_user', $user); + +use PEAR2\Net\RouterOS; +require_once 'system/autoload/PEAR2/Autoload.php'; + +switch ($action) { + + case 'activation': + $ui->display('user-activation.tpl'); + break; + + case 'activation-post': + $code = _post('code'); + + $v1 = ORM::for_table('tbl_voucher')->where('code',$code)->where('status',0)->find_one(); + + $c = ORM::for_table('tbl_customers')->find_one($user['id']); + $p = ORM::for_table('tbl_plans')->find_one($v1['id_plan']); + $b = ORM::for_table('tbl_user_recharges')->where('customer_id',$user['id'])->find_one(); + + $date_now = date("Y-m-d H:i:s"); + $date_only = date("Y-m-d"); + $time = date("H:i:s"); + + $mikrotik = Router::_info($v1['routers']); + $date_exp = date("Y-m-d", mktime(0,0,0,date("m"),date("d") + $p['validity'],date("Y"))); + + if ($v1){ + if($v1['type'] == 'Hotspot'){ + if($b){ + try { + $client = new RouterOS\Client($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + } catch (Exception $e) { + die('Unable to connect to the router.'); + } + $printRequest = new RouterOS\Request('/ip/hotspot/user/print'); + $printRequest->setArgument('.proplist', '.id'); + $printRequest->setQuery(RouterOS\Query::where('name', $c['username'])); + $id = $client->sendSync($printRequest)->getProperty('.id'); + + $setRequest = new RouterOS\Request('/ip/hotspot/user/remove'); + $setRequest->setArgument('numbers', $id); + $client->sendSync($setRequest); + + $addRequest = new RouterOS\Request('/ip/hotspot/user/add'); + $client->sendSync($addRequest + ->setArgument('name', $c['username']) + ->setArgument('profile', $p['name_plan']) + ->setArgument('password', $c['password']) + ); + + $b->customer_id = $user['id']; + $b->username = $c['username']; + $b->plan_id = $v1['id_plan']; + $b->namebp = $p['name_plan']; + $b->recharged_on = $date_only; + $b->expiration = $date_exp; + $b->time = $time; + $b->status = "on"; + $b->method = "voucher"; + $b->routers = $v1['routers']; + $b->type = "Hotspot"; + $b->save(); + + // insert table transactions + $t = ORM::for_table('tbl_transactions')->create(); + $t->invoice = "INV-"._raid(5); + $t->username = $c['username']; + $t->plan_name = $p['name_plan']; + $t->price = $p['price']; + $t->recharged_on = $date_only; + $t->expiration = $date_exp; + $t->time = $time; + $t->method = "voucher"; + $t->routers = $v1['routers']; + $t->type = "Hotspot"; + $t->save(); + + }else{ + try { + $client = new RouterOS\Client($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + } catch (Exception $e) { + die('Unable to connect to the router.'); + } + $addRequest = new RouterOS\Request('/ip/hotspot/user/add'); + $client->sendSync($addRequest + ->setArgument('name', $c['username']) + ->setArgument('profile', $p['name_plan']) + ->setArgument('password', $c['password']) + ); + + $d = ORM::for_table('tbl_user_recharges')->create(); + $d->customer_id = $user['id']; + $d->username = $c['username']; + $d->plan_id = $v1['id_plan']; + $d->namebp = $p['name_plan']; + $d->recharged_on = $date_only; + $d->expiration = $date_exp; + $d->time = $time; + $d->status = "on"; + $d->method = "voucher"; + $d->routers = $v1['routers']; + $d->type = "Hotspot"; + $d->save(); + + // insert table transactions + $t = ORM::for_table('tbl_transactions')->create(); + $t->invoice = "INV-"._raid(5); + $t->username = $c['username']; + $t->plan_name = $p['name_plan']; + $t->price = $p['price']; + $t->recharged_on = $date_only; + $t->expiration = $date_exp; + $t->time = $time; + $t->method = "voucher"; + $t->routers = $v1['routers']; + $t->type = "Hotspot"; + $t->save(); + + } + + $v1->status = "1"; + $v1->user = $c['username']; + $v1->save(); + + }else{ + if($b){ + try { + $client = new RouterOS\Client($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + } catch (Exception $e) { + die('Unable to connect to the router.'); + } + $printRequest = new RouterOS\Request('/ppp/secret/print'); + $printRequest->setArgument('.proplist', '.id'); + $printRequest->setQuery(RouterOS\Query::where('name', $c['username'])); + $id = $client->sendSync($printRequest)->getProperty('.id'); + + $setRequest = new RouterOS\Request('/ppp/secret/remove'); + $setRequest->setArgument('numbers', $id); + $client->sendSync($setRequest); + + $addRequest = new RouterOS\Request('/ppp/secret/add'); + $client->sendSync($addRequest + ->setArgument('name', $c['username']) + ->setArgument('service', 'pppoe') + ->setArgument('profile', $p['name_plan']) + ->setArgument('password', $c['password']) + ); + + $b->customer_id = $user['id']; + $b->username = $c['username']; + $b->plan_id = $v1['id_plan']; + $b->namebp = $p['name_plan']; + $b->recharged_on = $date_only; + $b->expiration = $date_exp; + $b->time = $time; + $b->status = "on"; + $b->method = "voucher"; + $b->routers = $v1['routers']; + $b->type = "PPPOE"; + $b->save(); + + // insert table transactions + $t = ORM::for_table('tbl_transactions')->create(); + $t->invoice = "INV-"._raid(5); + $t->username = $c['username']; + $t->plan_name = $p['name_plan']; + $t->price = $p['price']; + $t->recharged_on = $date_only; + $t->expiration = $date_exp; + $t->time = $time; + $t->method = "voucher"; + $t->routers = $v1['routers']; + $t->type = "PPPOE"; + $t->save(); + + }else{ + try { + $client = new RouterOS\Client($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); + } catch (Exception $e) { + die('Unable to connect to the router.'); + } + $addRequest = new RouterOS\Request('/ppp/secret/add'); + $client->sendSync($addRequest + ->setArgument('name', $c['username']) + ->setArgument('service', 'pppoe') + ->setArgument('profile', $p['name_plan']) + ->setArgument('password', $c['password']) + ); + + $d = ORM::for_table('tbl_user_recharges')->create(); + $d->customer_id = $user['id']; + $d->username = $c['username']; + $d->plan_id = $v1['id_plan']; + $d->namebp = $p['name_plan']; + $d->recharged_on = $date_only; + $d->expiration = $date_exp; + $d->time = $time; + $d->status = "on"; + $d->method = "voucher"; + $d->routers = $v1['routers']; + $d->type = "PPPOE"; + $d->save(); + + // insert table transactions + $t = ORM::for_table('tbl_transactions')->create(); + $t->invoice = "INV-"._raid(5); + $t->username = $c['username']; + $t->plan_name = $p['name_plan']; + $t->price = $p['price']; + $t->recharged_on = $date_only; + $t->expiration = $date_exp; + $t->time = $time; + $t->method = "voucher"; + $t->routers = $v1['routers']; + $t->type = "PPPOE"; + $t->save(); + } + + $v1->status = "1"; + $v1->user = $c['username']; + $v1->save(); + } + + r2(U."voucher/list-activated",'s',$_L['Activation_Vouchers_Successfully']); + }else{ + r2(U . 'voucher/activation', 'e', $_L['Voucher_Not_Valid']); + } + break; + + case 'list-activated': + $paginator = Paginator::bootstrap('tbl_transactions','username',$user['username']); + $d = ORM::for_table('tbl_transactions')->where('username',$user['username'])->offset($paginator['startpoint'])->limit($paginator['limit'])->order_by_desc('id')->find_many(); + + $ui->assign('d',$d); + $ui->assign('paginator',$paginator); + $ui->display('user-activation-list.tpl'); + + break; + + default: + $ui->display('404.tpl'); +} \ No newline at end of file diff --git a/system/cron.php b/system/cron.php new file mode 100644 index 0000000..98529af --- /dev/null +++ b/system/cron.php @@ -0,0 +1,109 @@ + +* @version 5.0 +* @copyright Copyright (C) 2014-2015 PHP Mikrotik Billing +* @license GNU General Public License version 2 or later; see LICENSE.txt +* @donate PayPal: iesien22@yahoo.com / Bank Mandiri: 130.00.1024957.4 +**/ + +require('config.php'); +require('orm.php'); + +use PEAR2\Net\RouterOS; +require_once 'autoload/PEAR2/Autoload.php'; + +ORM::configure("mysql:host=$db_host;dbname=$db_name"); +ORM::configure('username', $db_user); +ORM::configure('password', $db_password); +ORM::configure('return_result_sets', true); +ORM::configure('logging', true); + +$result = ORM::for_table('tbl_appconfig')->find_many(); +foreach($result as $value){ + $config[$value['setting']]=$value['value']; +} +date_default_timezone_set($config['timezone']); + +$d = ORM::for_table('tbl_user_recharges')->where('status','on')->find_many(); + +foreach ($d as $ds){ + if($ds['type'] == 'Hotspot'){ + $date_now = strtotime(date("Y-m-d H:i:s")); + $expiration = strtotime($ds['expiration'].' '.$ds['time']); + + if ($date_now >= $expiration){ + $u = ORM::for_table('tbl_user_recharges')->where('id',$ds['id'])->find_one(); + $c = ORM::for_table('tbl_customers')->where('id',$ds['customer_id'])->find_one(); + $m = ORM::for_table('tbl_routers')->where('name',$ds['routers'])->find_one(); + + try { + $client = new RouterOS\Client($m['ip_address'], $m['username'], $m['password']); + } catch (Exception $e) { + die('Unable to connect to the router.'); + } + $printRequest = new RouterOS\Request('/ip/hotspot/user/print'); + $printRequest->setArgument('.proplist', '.id'); + $printRequest->setQuery(RouterOS\Query::where('name', $c['username'])); + $id = $client->sendSync($printRequest)->getProperty('.id'); + + $setRequest = new RouterOS\Request('/ip/hotspot/user/set'); + $setRequest->setArgument('numbers', $id); + $setRequest->setArgument('limit-uptime', '00:00:05'); + $client->sendSync($setRequest); + + //remove hotspot active + $onlineRequest = new RouterOS\Request('/ip/hotspot/active/print'); + $onlineRequest->setArgument('.proplist', '.id'); + $onlineRequest->setQuery(RouterOS\Query::where('user', $c['username'])); + $id = $client->sendSync($onlineRequest)->getProperty('.id'); + + $removeRequest = new RouterOS\Request('/ip/hotspot/active/remove'); + $removeRequest->setArgument('numbers', $id); + $client->sendSync($removeRequest); + + //update database user dengan status off + $u->status = 'off'; + $u->save(); + } + }else{ + $date_now = strtotime(date("Y-m-d H:i:s")); + $expiration = strtotime($ds['expiration'].' '.$ds['time']); + + if ($date_now >= $expiration){ + $u = ORM::for_table('tbl_user_recharges')->where('id',$ds['id'])->find_one(); + $c = ORM::for_table('tbl_customers')->where('id',$ds['customer_id'])->find_one(); + $m = ORM::for_table('tbl_routers')->where('name',$ds['routers'])->find_one(); + + try { + $client = new RouterOS\Client($m['ip_address'], $m['username'], $m['password']); + } catch (Exception $e) { + die('Unable to connect to the router.'); + } + $printRequest = new RouterOS\Request('/ppp/secret/print'); + $printRequest->setArgument('.proplist', '.id'); + $printRequest->setQuery(RouterOS\Query::where('name', $c['username'])); + $id = $client->sendSync($printRequest)->getProperty('.id'); + + $setRequest = new RouterOS\Request('/ppp/secret/disable'); + $setRequest->setArgument('numbers', $id); + $client->sendSync($setRequest); + + //remove hotspot active + $onlineRequest = new RouterOS\Request('/ppp/secret/print'); + $onlineRequest->setArgument('.proplist', '.id'); + $onlineRequest->setQuery(RouterOS\Query::where('name', $c['username'])); + $id = $client->sendSync($onlineRequest)->getProperty('.id'); + + $removeRequest = new RouterOS\Request('/ppp/active/remove'); + $removeRequest->setArgument('numbers', $id); + $client->sendSync($removeRequest); + + $u->status = 'off'; + $u->save(); + } + } +} + +?> \ No newline at end of file diff --git a/system/index.html b/system/index.html new file mode 100644 index 0000000..9757970 --- /dev/null +++ b/system/index.html @@ -0,0 +1,8 @@ + + + 403 Forbidden + + +

Directory access is forbidden.

+ + \ No newline at end of file diff --git a/system/install/.DS_Store b/system/install/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..86637d29634b3d7a136e9939bf8f10338f3a4d8e GIT binary patch literal 6148 zcmeHKF=_)r43u&%3~5}Z+%Mz@i*a7y4+Ii0fy9QGq`oTe%F{9~rtt;}o*C)yXo-1wY6vzuaw&NJTkuZR6EceCu%1ZxL-vZEb8x0Cw8 zUhK_wf7sKfh?N3TKnh3!DIf*@s{nd7ZF!rhCCSTX0Hjw<96^Z(#?CKq7;w< zQw4_HuGs${;AiIlX_0nPKnh%y0>0R6H*367_14+TvDY^EBb+(ka2nQ8f)MQ(80{Dv fw&S}<%Dl!kp7+8rG0MS5IZ!_X)I}x*F0H^1D)Sd% literal 0 HcmV?d00001 diff --git a/system/install/css/bootstrap.css b/system/install/css/bootstrap.css new file mode 100644 index 0000000..037dd05 --- /dev/null +++ b/system/install/css/bootstrap.css @@ -0,0 +1,6203 @@ +/*! + * Bootstrap v3.2.0 (http://getbootstrap.com) + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ + +/*! normalize.css v3.0.1 | MIT License | git.io/normalize */ +html { + font-family: sans-serif; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} +body { + margin: 0; +} +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +nav, +section, +summary { + display: block; +} +audio, +canvas, +progress, +video { + display: inline-block; + vertical-align: baseline; +} +audio:not([controls]) { + display: none; + height: 0; +} +[hidden], +template { + display: none; +} +a { + background: transparent; +} +a:active, +a:hover { + outline: 0; +} +abbr[title] { + border-bottom: 1px dotted; +} +b, +strong { + font-weight: bold; +} +dfn { + font-style: italic; +} +h1 { + margin: .67em 0; + font-size: 2em; +} +mark { + color: #000; + background: #ff0; +} +small { + font-size: 80%; +} +sub, +sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; +} +sup { + top: -.5em; +} +sub { + bottom: -.25em; +} +img { + border: 0; +} +svg:not(:root) { + overflow: hidden; +} +figure { + margin: 1em 40px; +} +hr { + height: 0; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; +} +pre { + overflow: auto; +} +code, +kbd, +pre, +samp { + font-family: monospace, monospace; + font-size: 1em; +} +button, +input, +optgroup, +select, +textarea { + margin: 0; + font: inherit; + color: inherit; +} +button { + overflow: visible; +} +button, +select { + text-transform: none; +} +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; + cursor: pointer; +} +button[disabled], +html input[disabled] { + cursor: default; +} +button::-moz-focus-inner, +input::-moz-focus-inner { + padding: 0; + border: 0; +} +input { + line-height: normal; +} +input[type="checkbox"], +input[type="radio"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 0; +} +input[type="number"]::-webkit-inner-spin-button, +input[type="number"]::-webkit-outer-spin-button { + height: auto; +} +input[type="search"] { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + -webkit-appearance: textfield; +} +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} +fieldset { + padding: .35em .625em .75em; + margin: 0 2px; + border: 1px solid #c0c0c0; +} +legend { + padding: 0; + border: 0; +} +textarea { + overflow: auto; +} +optgroup { + font-weight: bold; +} +table { + border-spacing: 0; + border-collapse: collapse; +} +td, +th { + padding: 0; +} +@media print { + * { + color: #000 !important; + text-shadow: none !important; + background: transparent !important; + -webkit-box-shadow: none !important; + box-shadow: none !important; + } + a, + a:visited { + text-decoration: underline; + } + a[href]:after { + content: " (" attr(href) ")"; + } + abbr[title]:after { + content: " (" attr(title) ")"; + } + a[href^="javascript:"]:after, + a[href^="#"]:after { + content: ""; + } + pre, + blockquote { + border: 1px solid #999; + + page-break-inside: avoid; + } + thead { + display: table-header-group; + } + tr, + img { + page-break-inside: avoid; + } + img { + max-width: 100% !important; + } + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + h2, + h3 { + page-break-after: avoid; + } + select { + background: #fff !important; + } + .navbar { + display: none; + } + .table td, + .table th { + background-color: #fff !important; + } + .btn > .caret, + .dropup > .btn > .caret { + border-top-color: #000 !important; + } + .label { + border: 1px solid #000; + } + .table { + border-collapse: collapse !important; + } + .table-bordered th, + .table-bordered td { + border: 1px solid #ddd !important; + } +} +@font-face { + font-family: 'Glyphicons Halflings'; + + src: url('../fonts/glyphicons-halflings-regular.eot'); + src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); +} +.glyphicon { + position: relative; + top: 1px; + display: inline-block; + font-family: 'Glyphicons Halflings'; + font-style: normal; + font-weight: normal; + line-height: 1; + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.glyphicon-asterisk:before { + content: "\2a"; +} +.glyphicon-plus:before { + content: "\2b"; +} +.glyphicon-euro:before { + content: "\20ac"; +} +.glyphicon-minus:before { + content: "\2212"; +} +.glyphicon-cloud:before { + content: "\2601"; +} +.glyphicon-envelope:before { + content: "\2709"; +} +.glyphicon-pencil:before { + content: "\270f"; +} +.glyphicon-glass:before { + content: "\e001"; +} +.glyphicon-music:before { + content: "\e002"; +} +.glyphicon-search:before { + content: "\e003"; +} +.glyphicon-heart:before { + content: "\e005"; +} +.glyphicon-star:before { + content: "\e006"; +} +.glyphicon-star-empty:before { + content: "\e007"; +} +.glyphicon-user:before { + content: "\e008"; +} +.glyphicon-film:before { + content: "\e009"; +} +.glyphicon-th-large:before { + content: "\e010"; +} +.glyphicon-th:before { + content: "\e011"; +} +.glyphicon-th-list:before { + content: "\e012"; +} +.glyphicon-ok:before { + content: "\e013"; +} +.glyphicon-remove:before { + content: "\e014"; +} +.glyphicon-zoom-in:before { + content: "\e015"; +} +.glyphicon-zoom-out:before { + content: "\e016"; +} +.glyphicon-off:before { + content: "\e017"; +} +.glyphicon-signal:before { + content: "\e018"; +} +.glyphicon-cog:before { + content: "\e019"; +} +.glyphicon-trash:before { + content: "\e020"; +} +.glyphicon-home:before { + content: "\e021"; +} +.glyphicon-file:before { + content: "\e022"; +} +.glyphicon-time:before { + content: "\e023"; +} +.glyphicon-road:before { + content: "\e024"; +} +.glyphicon-download-alt:before { + content: "\e025"; +} +.glyphicon-download:before { + content: "\e026"; +} +.glyphicon-upload:before { + content: "\e027"; +} +.glyphicon-inbox:before { + content: "\e028"; +} +.glyphicon-play-circle:before { + content: "\e029"; +} +.glyphicon-repeat:before { + content: "\e030"; +} +.glyphicon-refresh:before { + content: "\e031"; +} +.glyphicon-list-alt:before { + content: "\e032"; +} +.glyphicon-lock:before { + content: "\e033"; +} +.glyphicon-flag:before { + content: "\e034"; +} +.glyphicon-headphones:before { + content: "\e035"; +} +.glyphicon-volume-off:before { + content: "\e036"; +} +.glyphicon-volume-down:before { + content: "\e037"; +} +.glyphicon-volume-up:before { + content: "\e038"; +} +.glyphicon-qrcode:before { + content: "\e039"; +} +.glyphicon-barcode:before { + content: "\e040"; +} +.glyphicon-tag:before { + content: "\e041"; +} +.glyphicon-tags:before { + content: "\e042"; +} +.glyphicon-book:before { + content: "\e043"; +} +.glyphicon-bookmark:before { + content: "\e044"; +} +.glyphicon-print:before { + content: "\e045"; +} +.glyphicon-camera:before { + content: "\e046"; +} +.glyphicon-font:before { + content: "\e047"; +} +.glyphicon-bold:before { + content: "\e048"; +} +.glyphicon-italic:before { + content: "\e049"; +} +.glyphicon-text-height:before { + content: "\e050"; +} +.glyphicon-text-width:before { + content: "\e051"; +} +.glyphicon-align-left:before { + content: "\e052"; +} +.glyphicon-align-center:before { + content: "\e053"; +} +.glyphicon-align-right:before { + content: "\e054"; +} +.glyphicon-align-justify:before { + content: "\e055"; +} +.glyphicon-list:before { + content: "\e056"; +} +.glyphicon-indent-left:before { + content: "\e057"; +} +.glyphicon-indent-right:before { + content: "\e058"; +} +.glyphicon-facetime-video:before { + content: "\e059"; +} +.glyphicon-picture:before { + content: "\e060"; +} +.glyphicon-map-marker:before { + content: "\e062"; +} +.glyphicon-adjust:before { + content: "\e063"; +} +.glyphicon-tint:before { + content: "\e064"; +} +.glyphicon-edit:before { + content: "\e065"; +} +.glyphicon-share:before { + content: "\e066"; +} +.glyphicon-check:before { + content: "\e067"; +} +.glyphicon-move:before { + content: "\e068"; +} +.glyphicon-step-backward:before { + content: "\e069"; +} +.glyphicon-fast-backward:before { + content: "\e070"; +} +.glyphicon-backward:before { + content: "\e071"; +} +.glyphicon-play:before { + content: "\e072"; +} +.glyphicon-pause:before { + content: "\e073"; +} +.glyphicon-stop:before { + content: "\e074"; +} +.glyphicon-forward:before { + content: "\e075"; +} +.glyphicon-fast-forward:before { + content: "\e076"; +} +.glyphicon-step-forward:before { + content: "\e077"; +} +.glyphicon-eject:before { + content: "\e078"; +} +.glyphicon-chevron-left:before { + content: "\e079"; +} +.glyphicon-chevron-right:before { + content: "\e080"; +} +.glyphicon-plus-sign:before { + content: "\e081"; +} +.glyphicon-minus-sign:before { + content: "\e082"; +} +.glyphicon-remove-sign:before { + content: "\e083"; +} +.glyphicon-ok-sign:before { + content: "\e084"; +} +.glyphicon-question-sign:before { + content: "\e085"; +} +.glyphicon-info-sign:before { + content: "\e086"; +} +.glyphicon-screenshot:before { + content: "\e087"; +} +.glyphicon-remove-circle:before { + content: "\e088"; +} +.glyphicon-ok-circle:before { + content: "\e089"; +} +.glyphicon-ban-circle:before { + content: "\e090"; +} +.glyphicon-arrow-left:before { + content: "\e091"; +} +.glyphicon-arrow-right:before { + content: "\e092"; +} +.glyphicon-arrow-up:before { + content: "\e093"; +} +.glyphicon-arrow-down:before { + content: "\e094"; +} +.glyphicon-share-alt:before { + content: "\e095"; +} +.glyphicon-resize-full:before { + content: "\e096"; +} +.glyphicon-resize-small:before { + content: "\e097"; +} +.glyphicon-exclamation-sign:before { + content: "\e101"; +} +.glyphicon-gift:before { + content: "\e102"; +} +.glyphicon-leaf:before { + content: "\e103"; +} +.glyphicon-fire:before { + content: "\e104"; +} +.glyphicon-eye-open:before { + content: "\e105"; +} +.glyphicon-eye-close:before { + content: "\e106"; +} +.glyphicon-warning-sign:before { + content: "\e107"; +} +.glyphicon-plane:before { + content: "\e108"; +} +.glyphicon-calendar:before { + content: "\e109"; +} +.glyphicon-random:before { + content: "\e110"; +} +.glyphicon-comment:before { + content: "\e111"; +} +.glyphicon-magnet:before { + content: "\e112"; +} +.glyphicon-chevron-up:before { + content: "\e113"; +} +.glyphicon-chevron-down:before { + content: "\e114"; +} +.glyphicon-retweet:before { + content: "\e115"; +} +.glyphicon-shopping-cart:before { + content: "\e116"; +} +.glyphicon-folder-close:before { + content: "\e117"; +} +.glyphicon-folder-open:before { + content: "\e118"; +} +.glyphicon-resize-vertical:before { + content: "\e119"; +} +.glyphicon-resize-horizontal:before { + content: "\e120"; +} +.glyphicon-hdd:before { + content: "\e121"; +} +.glyphicon-bullhorn:before { + content: "\e122"; +} +.glyphicon-bell:before { + content: "\e123"; +} +.glyphicon-certificate:before { + content: "\e124"; +} +.glyphicon-thumbs-up:before { + content: "\e125"; +} +.glyphicon-thumbs-down:before { + content: "\e126"; +} +.glyphicon-hand-right:before { + content: "\e127"; +} +.glyphicon-hand-left:before { + content: "\e128"; +} +.glyphicon-hand-up:before { + content: "\e129"; +} +.glyphicon-hand-down:before { + content: "\e130"; +} +.glyphicon-circle-arrow-right:before { + content: "\e131"; +} +.glyphicon-circle-arrow-left:before { + content: "\e132"; +} +.glyphicon-circle-arrow-up:before { + content: "\e133"; +} +.glyphicon-circle-arrow-down:before { + content: "\e134"; +} +.glyphicon-globe:before { + content: "\e135"; +} +.glyphicon-wrench:before { + content: "\e136"; +} +.glyphicon-tasks:before { + content: "\e137"; +} +.glyphicon-filter:before { + content: "\e138"; +} +.glyphicon-briefcase:before { + content: "\e139"; +} +.glyphicon-fullscreen:before { + content: "\e140"; +} +.glyphicon-dashboard:before { + content: "\e141"; +} +.glyphicon-paperclip:before { + content: "\e142"; +} +.glyphicon-heart-empty:before { + content: "\e143"; +} +.glyphicon-link:before { + content: "\e144"; +} +.glyphicon-phone:before { + content: "\e145"; +} +.glyphicon-pushpin:before { + content: "\e146"; +} +.glyphicon-usd:before { + content: "\e148"; +} +.glyphicon-gbp:before { + content: "\e149"; +} +.glyphicon-sort:before { + content: "\e150"; +} +.glyphicon-sort-by-alphabet:before { + content: "\e151"; +} +.glyphicon-sort-by-alphabet-alt:before { + content: "\e152"; +} +.glyphicon-sort-by-order:before { + content: "\e153"; +} +.glyphicon-sort-by-order-alt:before { + content: "\e154"; +} +.glyphicon-sort-by-attributes:before { + content: "\e155"; +} +.glyphicon-sort-by-attributes-alt:before { + content: "\e156"; +} +.glyphicon-unchecked:before { + content: "\e157"; +} +.glyphicon-expand:before { + content: "\e158"; +} +.glyphicon-collapse-down:before { + content: "\e159"; +} +.glyphicon-collapse-up:before { + content: "\e160"; +} +.glyphicon-log-in:before { + content: "\e161"; +} +.glyphicon-flash:before { + content: "\e162"; +} +.glyphicon-log-out:before { + content: "\e163"; +} +.glyphicon-new-window:before { + content: "\e164"; +} +.glyphicon-record:before { + content: "\e165"; +} +.glyphicon-save:before { + content: "\e166"; +} +.glyphicon-open:before { + content: "\e167"; +} +.glyphicon-saved:before { + content: "\e168"; +} +.glyphicon-import:before { + content: "\e169"; +} +.glyphicon-export:before { + content: "\e170"; +} +.glyphicon-send:before { + content: "\e171"; +} +.glyphicon-floppy-disk:before { + content: "\e172"; +} +.glyphicon-floppy-saved:before { + content: "\e173"; +} +.glyphicon-floppy-remove:before { + content: "\e174"; +} +.glyphicon-floppy-save:before { + content: "\e175"; +} +.glyphicon-floppy-open:before { + content: "\e176"; +} +.glyphicon-credit-card:before { + content: "\e177"; +} +.glyphicon-transfer:before { + content: "\e178"; +} +.glyphicon-cutlery:before { + content: "\e179"; +} +.glyphicon-header:before { + content: "\e180"; +} +.glyphicon-compressed:before { + content: "\e181"; +} +.glyphicon-earphone:before { + content: "\e182"; +} +.glyphicon-phone-alt:before { + content: "\e183"; +} +.glyphicon-tower:before { + content: "\e184"; +} +.glyphicon-stats:before { + content: "\e185"; +} +.glyphicon-sd-video:before { + content: "\e186"; +} +.glyphicon-hd-video:before { + content: "\e187"; +} +.glyphicon-subtitles:before { + content: "\e188"; +} +.glyphicon-sound-stereo:before { + content: "\e189"; +} +.glyphicon-sound-dolby:before { + content: "\e190"; +} +.glyphicon-sound-5-1:before { + content: "\e191"; +} +.glyphicon-sound-6-1:before { + content: "\e192"; +} +.glyphicon-sound-7-1:before { + content: "\e193"; +} +.glyphicon-copyright-mark:before { + content: "\e194"; +} +.glyphicon-registration-mark:before { + content: "\e195"; +} +.glyphicon-cloud-download:before { + content: "\e197"; +} +.glyphicon-cloud-upload:before { + content: "\e198"; +} +.glyphicon-tree-conifer:before { + content: "\e199"; +} +.glyphicon-tree-deciduous:before { + content: "\e200"; +} +* { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +*:before, +*:after { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +html { + font-size: 10px; + + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} +body { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 1.42857143; + color: #333; + background-color: #fff; +} +input, +button, +select, +textarea { + font-family: inherit; + font-size: inherit; + line-height: inherit; +} +a { + color: #428bca; + text-decoration: none; +} +a:hover, +a:focus { + color: #2a6496; + text-decoration: underline; +} +a:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +figure { + margin: 0; +} +img { + vertical-align: middle; +} +.img-responsive, +.thumbnail > img, +.thumbnail a > img, +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + display: block; + width: 100% \9; + max-width: 100%; + height: auto; +} +.img-rounded { + border-radius: 6px; +} +.img-thumbnail { + display: inline-block; + width: 100% \9; + max-width: 100%; + height: auto; + padding: 4px; + line-height: 1.42857143; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 4px; + -webkit-transition: all .2s ease-in-out; + -o-transition: all .2s ease-in-out; + transition: all .2s ease-in-out; +} +.img-circle { + border-radius: 50%; +} +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-top: 1px solid #eee; +} +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; +} +.sr-only-focusable:active, +.sr-only-focusable:focus { + position: static; + width: auto; + height: auto; + margin: 0; + overflow: visible; + clip: auto; +} +h1, +h2, +h3, +h4, +h5, +h6, +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { + font-family: inherit; + font-weight: 500; + line-height: 1.1; + color: inherit; +} +h1 small, +h2 small, +h3 small, +h4 small, +h5 small, +h6 small, +.h1 small, +.h2 small, +.h3 small, +.h4 small, +.h5 small, +.h6 small, +h1 .small, +h2 .small, +h3 .small, +h4 .small, +h5 .small, +h6 .small, +.h1 .small, +.h2 .small, +.h3 .small, +.h4 .small, +.h5 .small, +.h6 .small { + font-weight: normal; + line-height: 1; + color: #777; +} +h1, +.h1, +h2, +.h2, +h3, +.h3 { + margin-top: 20px; + margin-bottom: 10px; +} +h1 small, +.h1 small, +h2 small, +.h2 small, +h3 small, +.h3 small, +h1 .small, +.h1 .small, +h2 .small, +.h2 .small, +h3 .small, +.h3 .small { + font-size: 65%; +} +h4, +.h4, +h5, +.h5, +h6, +.h6 { + margin-top: 10px; + margin-bottom: 10px; +} +h4 small, +.h4 small, +h5 small, +.h5 small, +h6 small, +.h6 small, +h4 .small, +.h4 .small, +h5 .small, +.h5 .small, +h6 .small, +.h6 .small { + font-size: 75%; +} +h1, +.h1 { + font-size: 36px; +} +h2, +.h2 { + font-size: 30px; +} +h3, +.h3 { + font-size: 24px; +} +h4, +.h4 { + font-size: 18px; +} +h5, +.h5 { + font-size: 14px; +} +h6, +.h6 { + font-size: 12px; +} +p { + margin: 0 0 10px; +} +.lead { + margin-bottom: 20px; + font-size: 16px; + font-weight: 300; + line-height: 1.4; +} +@media (min-width: 768px) { + .lead { + font-size: 21px; + } +} +small, +.small { + font-size: 85%; +} +cite { + font-style: normal; +} +mark, +.mark { + padding: .2em; + background-color: #fcf8e3; +} +.text-left { + text-align: left; +} +.text-right { + text-align: right; +} +.text-center { + text-align: center; +} +.text-justify { + text-align: justify; +} +.text-nowrap { + white-space: nowrap; +} +.text-lowercase { + text-transform: lowercase; +} +.text-uppercase { + text-transform: uppercase; +} +.text-capitalize { + text-transform: capitalize; +} +.text-muted { + color: #777; +} +.text-primary { + color: #428bca; +} +a.text-primary:hover { + color: #3071a9; +} +.text-success { + color: #3c763d; +} +a.text-success:hover { + color: #2b542c; +} +.text-info { + color: #31708f; +} +a.text-info:hover { + color: #245269; +} +.text-warning { + color: #8a6d3b; +} +a.text-warning:hover { + color: #66512c; +} +.text-danger { + color: #a94442; +} +a.text-danger:hover { + color: #843534; +} +.bg-primary { + color: #fff; + background-color: #428bca; +} +a.bg-primary:hover { + background-color: #3071a9; +} +.bg-success { + background-color: #dff0d8; +} +a.bg-success:hover { + background-color: #c1e2b3; +} +.bg-info { + background-color: #d9edf7; +} +a.bg-info:hover { + background-color: #afd9ee; +} +.bg-warning { + background-color: #fcf8e3; +} +a.bg-warning:hover { + background-color: #f7ecb5; +} +.bg-danger { + background-color: #f2dede; +} +a.bg-danger:hover { + background-color: #e4b9b9; +} +.page-header { + padding-bottom: 9px; + margin: 40px 0 20px; + border-bottom: 1px solid #eee; +} +ul, +ol { + margin-top: 0; + margin-bottom: 10px; +} +ul ul, +ol ul, +ul ol, +ol ol { + margin-bottom: 0; +} +.list-unstyled { + padding-left: 0; + list-style: none; +} +.list-inline { + padding-left: 0; + margin-left: -5px; + list-style: none; +} +.list-inline > li { + display: inline-block; + padding-right: 5px; + padding-left: 5px; +} +dl { + margin-top: 0; + margin-bottom: 20px; +} +dt, +dd { + line-height: 1.42857143; +} +dt { + font-weight: bold; +} +dd { + margin-left: 0; +} +@media (min-width: 768px) { + .dl-horizontal dt { + float: left; + width: 160px; + overflow: hidden; + clear: left; + text-align: right; + text-overflow: ellipsis; + white-space: nowrap; + } + .dl-horizontal dd { + margin-left: 180px; + } +} +abbr[title], +abbr[data-original-title] { + cursor: help; + border-bottom: 1px dotted #777; +} +.initialism { + font-size: 90%; + text-transform: uppercase; +} +blockquote { + padding: 10px 20px; + margin: 0 0 20px; + font-size: 17.5px; + border-left: 5px solid #eee; +} +blockquote p:last-child, +blockquote ul:last-child, +blockquote ol:last-child { + margin-bottom: 0; +} +blockquote footer, +blockquote small, +blockquote .small { + display: block; + font-size: 80%; + line-height: 1.42857143; + color: #777; +} +blockquote footer:before, +blockquote small:before, +blockquote .small:before { + content: '\2014 \00A0'; +} +.blockquote-reverse, +blockquote.pull-right { + padding-right: 15px; + padding-left: 0; + text-align: right; + border-right: 5px solid #eee; + border-left: 0; +} +.blockquote-reverse footer:before, +blockquote.pull-right footer:before, +.blockquote-reverse small:before, +blockquote.pull-right small:before, +.blockquote-reverse .small:before, +blockquote.pull-right .small:before { + content: ''; +} +.blockquote-reverse footer:after, +blockquote.pull-right footer:after, +.blockquote-reverse small:after, +blockquote.pull-right small:after, +.blockquote-reverse .small:after, +blockquote.pull-right .small:after { + content: '\00A0 \2014'; +} +blockquote:before, +blockquote:after { + content: ""; +} +address { + margin-bottom: 20px; + font-style: normal; + line-height: 1.42857143; +} +code, +kbd, +pre, +samp { + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; +} +code { + padding: 2px 4px; + font-size: 90%; + color: #c7254e; + background-color: #f9f2f4; + border-radius: 4px; +} +kbd { + padding: 2px 4px; + font-size: 90%; + color: #fff; + background-color: #333; + border-radius: 3px; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); +} +kbd kbd { + padding: 0; + font-size: 100%; + -webkit-box-shadow: none; + box-shadow: none; +} +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 1.42857143; + color: #333; + word-break: break-all; + word-wrap: break-word; + background-color: #f5f5f5; + border: 1px solid #ccc; + border-radius: 4px; +} +pre code { + padding: 0; + font-size: inherit; + color: inherit; + white-space: pre-wrap; + background-color: transparent; + border-radius: 0; +} +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; +} +.container { + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} +@media (min-width: 768px) { + .container { + width: 750px; + } +} +@media (min-width: 992px) { + .container { + width: 970px; + } +} +@media (min-width: 1200px) { + .container { + width: 1170px; + } +} +.container-fluid { + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} +.row { + margin-right: -15px; + margin-left: -15px; +} +.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 { + position: relative; + min-height: 1px; + padding-right: 15px; + padding-left: 15px; +} +.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 { + float: left; +} +.col-xs-12 { + width: 100%; +} +.col-xs-11 { + width: 91.66666667%; +} +.col-xs-10 { + width: 83.33333333%; +} +.col-xs-9 { + width: 75%; +} +.col-xs-8 { + width: 66.66666667%; +} +.col-xs-7 { + width: 58.33333333%; +} +.col-xs-6 { + width: 50%; +} +.col-xs-5 { + width: 41.66666667%; +} +.col-xs-4 { + width: 33.33333333%; +} +.col-xs-3 { + width: 25%; +} +.col-xs-2 { + width: 16.66666667%; +} +.col-xs-1 { + width: 8.33333333%; +} +.col-xs-pull-12 { + right: 100%; +} +.col-xs-pull-11 { + right: 91.66666667%; +} +.col-xs-pull-10 { + right: 83.33333333%; +} +.col-xs-pull-9 { + right: 75%; +} +.col-xs-pull-8 { + right: 66.66666667%; +} +.col-xs-pull-7 { + right: 58.33333333%; +} +.col-xs-pull-6 { + right: 50%; +} +.col-xs-pull-5 { + right: 41.66666667%; +} +.col-xs-pull-4 { + right: 33.33333333%; +} +.col-xs-pull-3 { + right: 25%; +} +.col-xs-pull-2 { + right: 16.66666667%; +} +.col-xs-pull-1 { + right: 8.33333333%; +} +.col-xs-pull-0 { + right: auto; +} +.col-xs-push-12 { + left: 100%; +} +.col-xs-push-11 { + left: 91.66666667%; +} +.col-xs-push-10 { + left: 83.33333333%; +} +.col-xs-push-9 { + left: 75%; +} +.col-xs-push-8 { + left: 66.66666667%; +} +.col-xs-push-7 { + left: 58.33333333%; +} +.col-xs-push-6 { + left: 50%; +} +.col-xs-push-5 { + left: 41.66666667%; +} +.col-xs-push-4 { + left: 33.33333333%; +} +.col-xs-push-3 { + left: 25%; +} +.col-xs-push-2 { + left: 16.66666667%; +} +.col-xs-push-1 { + left: 8.33333333%; +} +.col-xs-push-0 { + left: auto; +} +.col-xs-offset-12 { + margin-left: 100%; +} +.col-xs-offset-11 { + margin-left: 91.66666667%; +} +.col-xs-offset-10 { + margin-left: 83.33333333%; +} +.col-xs-offset-9 { + margin-left: 75%; +} +.col-xs-offset-8 { + margin-left: 66.66666667%; +} +.col-xs-offset-7 { + margin-left: 58.33333333%; +} +.col-xs-offset-6 { + margin-left: 50%; +} +.col-xs-offset-5 { + margin-left: 41.66666667%; +} +.col-xs-offset-4 { + margin-left: 33.33333333%; +} +.col-xs-offset-3 { + margin-left: 25%; +} +.col-xs-offset-2 { + margin-left: 16.66666667%; +} +.col-xs-offset-1 { + margin-left: 8.33333333%; +} +.col-xs-offset-0 { + margin-left: 0; +} +@media (min-width: 768px) { + .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 { + float: left; + } + .col-sm-12 { + width: 100%; + } + .col-sm-11 { + width: 91.66666667%; + } + .col-sm-10 { + width: 83.33333333%; + } + .col-sm-9 { + width: 75%; + } + .col-sm-8 { + width: 66.66666667%; + } + .col-sm-7 { + width: 58.33333333%; + } + .col-sm-6 { + width: 50%; + } + .col-sm-5 { + width: 41.66666667%; + } + .col-sm-4 { + width: 33.33333333%; + } + .col-sm-3 { + width: 25%; + } + .col-sm-2 { + width: 16.66666667%; + } + .col-sm-1 { + width: 8.33333333%; + } + .col-sm-pull-12 { + right: 100%; + } + .col-sm-pull-11 { + right: 91.66666667%; + } + .col-sm-pull-10 { + right: 83.33333333%; + } + .col-sm-pull-9 { + right: 75%; + } + .col-sm-pull-8 { + right: 66.66666667%; + } + .col-sm-pull-7 { + right: 58.33333333%; + } + .col-sm-pull-6 { + right: 50%; + } + .col-sm-pull-5 { + right: 41.66666667%; + } + .col-sm-pull-4 { + right: 33.33333333%; + } + .col-sm-pull-3 { + right: 25%; + } + .col-sm-pull-2 { + right: 16.66666667%; + } + .col-sm-pull-1 { + right: 8.33333333%; + } + .col-sm-pull-0 { + right: auto; + } + .col-sm-push-12 { + left: 100%; + } + .col-sm-push-11 { + left: 91.66666667%; + } + .col-sm-push-10 { + left: 83.33333333%; + } + .col-sm-push-9 { + left: 75%; + } + .col-sm-push-8 { + left: 66.66666667%; + } + .col-sm-push-7 { + left: 58.33333333%; + } + .col-sm-push-6 { + left: 50%; + } + .col-sm-push-5 { + left: 41.66666667%; + } + .col-sm-push-4 { + left: 33.33333333%; + } + .col-sm-push-3 { + left: 25%; + } + .col-sm-push-2 { + left: 16.66666667%; + } + .col-sm-push-1 { + left: 8.33333333%; + } + .col-sm-push-0 { + left: auto; + } + .col-sm-offset-12 { + margin-left: 100%; + } + .col-sm-offset-11 { + margin-left: 91.66666667%; + } + .col-sm-offset-10 { + margin-left: 83.33333333%; + } + .col-sm-offset-9 { + margin-left: 75%; + } + .col-sm-offset-8 { + margin-left: 66.66666667%; + } + .col-sm-offset-7 { + margin-left: 58.33333333%; + } + .col-sm-offset-6 { + margin-left: 50%; + } + .col-sm-offset-5 { + margin-left: 41.66666667%; + } + .col-sm-offset-4 { + margin-left: 33.33333333%; + } + .col-sm-offset-3 { + margin-left: 25%; + } + .col-sm-offset-2 { + margin-left: 16.66666667%; + } + .col-sm-offset-1 { + margin-left: 8.33333333%; + } + .col-sm-offset-0 { + margin-left: 0; + } +} +@media (min-width: 992px) { + .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 { + float: left; + } + .col-md-12 { + width: 100%; + } + .col-md-11 { + width: 91.66666667%; + } + .col-md-10 { + width: 83.33333333%; + } + .col-md-9 { + width: 75%; + } + .col-md-8 { + width: 66.66666667%; + } + .col-md-7 { + width: 58.33333333%; + } + .col-md-6 { + width: 50%; + } + .col-md-5 { + width: 41.66666667%; + } + .col-md-4 { + width: 33.33333333%; + } + .col-md-3 { + width: 25%; + } + .col-md-2 { + width: 16.66666667%; + } + .col-md-1 { + width: 8.33333333%; + } + .col-md-pull-12 { + right: 100%; + } + .col-md-pull-11 { + right: 91.66666667%; + } + .col-md-pull-10 { + right: 83.33333333%; + } + .col-md-pull-9 { + right: 75%; + } + .col-md-pull-8 { + right: 66.66666667%; + } + .col-md-pull-7 { + right: 58.33333333%; + } + .col-md-pull-6 { + right: 50%; + } + .col-md-pull-5 { + right: 41.66666667%; + } + .col-md-pull-4 { + right: 33.33333333%; + } + .col-md-pull-3 { + right: 25%; + } + .col-md-pull-2 { + right: 16.66666667%; + } + .col-md-pull-1 { + right: 8.33333333%; + } + .col-md-pull-0 { + right: auto; + } + .col-md-push-12 { + left: 100%; + } + .col-md-push-11 { + left: 91.66666667%; + } + .col-md-push-10 { + left: 83.33333333%; + } + .col-md-push-9 { + left: 75%; + } + .col-md-push-8 { + left: 66.66666667%; + } + .col-md-push-7 { + left: 58.33333333%; + } + .col-md-push-6 { + left: 50%; + } + .col-md-push-5 { + left: 41.66666667%; + } + .col-md-push-4 { + left: 33.33333333%; + } + .col-md-push-3 { + left: 25%; + } + .col-md-push-2 { + left: 16.66666667%; + } + .col-md-push-1 { + left: 8.33333333%; + } + .col-md-push-0 { + left: auto; + } + .col-md-offset-12 { + margin-left: 100%; + } + .col-md-offset-11 { + margin-left: 91.66666667%; + } + .col-md-offset-10 { + margin-left: 83.33333333%; + } + .col-md-offset-9 { + margin-left: 75%; + } + .col-md-offset-8 { + margin-left: 66.66666667%; + } + .col-md-offset-7 { + margin-left: 58.33333333%; + } + .col-md-offset-6 { + margin-left: 50%; + } + .col-md-offset-5 { + margin-left: 41.66666667%; + } + .col-md-offset-4 { + margin-left: 33.33333333%; + } + .col-md-offset-3 { + margin-left: 25%; + } + .col-md-offset-2 { + margin-left: 16.66666667%; + } + .col-md-offset-1 { + margin-left: 8.33333333%; + } + .col-md-offset-0 { + margin-left: 0; + } +} +@media (min-width: 1200px) { + .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 { + float: left; + } + .col-lg-12 { + width: 100%; + } + .col-lg-11 { + width: 91.66666667%; + } + .col-lg-10 { + width: 83.33333333%; + } + .col-lg-9 { + width: 75%; + } + .col-lg-8 { + width: 66.66666667%; + } + .col-lg-7 { + width: 58.33333333%; + } + .col-lg-6 { + width: 50%; + } + .col-lg-5 { + width: 41.66666667%; + } + .col-lg-4 { + width: 33.33333333%; + } + .col-lg-3 { + width: 25%; + } + .col-lg-2 { + width: 16.66666667%; + } + .col-lg-1 { + width: 8.33333333%; + } + .col-lg-pull-12 { + right: 100%; + } + .col-lg-pull-11 { + right: 91.66666667%; + } + .col-lg-pull-10 { + right: 83.33333333%; + } + .col-lg-pull-9 { + right: 75%; + } + .col-lg-pull-8 { + right: 66.66666667%; + } + .col-lg-pull-7 { + right: 58.33333333%; + } + .col-lg-pull-6 { + right: 50%; + } + .col-lg-pull-5 { + right: 41.66666667%; + } + .col-lg-pull-4 { + right: 33.33333333%; + } + .col-lg-pull-3 { + right: 25%; + } + .col-lg-pull-2 { + right: 16.66666667%; + } + .col-lg-pull-1 { + right: 8.33333333%; + } + .col-lg-pull-0 { + right: auto; + } + .col-lg-push-12 { + left: 100%; + } + .col-lg-push-11 { + left: 91.66666667%; + } + .col-lg-push-10 { + left: 83.33333333%; + } + .col-lg-push-9 { + left: 75%; + } + .col-lg-push-8 { + left: 66.66666667%; + } + .col-lg-push-7 { + left: 58.33333333%; + } + .col-lg-push-6 { + left: 50%; + } + .col-lg-push-5 { + left: 41.66666667%; + } + .col-lg-push-4 { + left: 33.33333333%; + } + .col-lg-push-3 { + left: 25%; + } + .col-lg-push-2 { + left: 16.66666667%; + } + .col-lg-push-1 { + left: 8.33333333%; + } + .col-lg-push-0 { + left: auto; + } + .col-lg-offset-12 { + margin-left: 100%; + } + .col-lg-offset-11 { + margin-left: 91.66666667%; + } + .col-lg-offset-10 { + margin-left: 83.33333333%; + } + .col-lg-offset-9 { + margin-left: 75%; + } + .col-lg-offset-8 { + margin-left: 66.66666667%; + } + .col-lg-offset-7 { + margin-left: 58.33333333%; + } + .col-lg-offset-6 { + margin-left: 50%; + } + .col-lg-offset-5 { + margin-left: 41.66666667%; + } + .col-lg-offset-4 { + margin-left: 33.33333333%; + } + .col-lg-offset-3 { + margin-left: 25%; + } + .col-lg-offset-2 { + margin-left: 16.66666667%; + } + .col-lg-offset-1 { + margin-left: 8.33333333%; + } + .col-lg-offset-0 { + margin-left: 0; + } +} +table { + background-color: transparent; +} +th { + text-align: left; +} +.table { + width: 100%; + max-width: 100%; + margin-bottom: 20px; +} +.table > thead > tr > th, +.table > tbody > tr > th, +.table > tfoot > tr > th, +.table > thead > tr > td, +.table > tbody > tr > td, +.table > tfoot > tr > td { + padding: 8px; + line-height: 1.42857143; + vertical-align: top; + border-top: 1px solid #ddd; +} +.table > thead > tr > th { + vertical-align: bottom; + border-bottom: 2px solid #ddd; +} +.table > caption + thead > tr:first-child > th, +.table > colgroup + thead > tr:first-child > th, +.table > thead:first-child > tr:first-child > th, +.table > caption + thead > tr:first-child > td, +.table > colgroup + thead > tr:first-child > td, +.table > thead:first-child > tr:first-child > td { + border-top: 0; +} +.table > tbody + tbody { + border-top: 2px solid #ddd; +} +.table .table { + background-color: #fff; +} +.table-condensed > thead > tr > th, +.table-condensed > tbody > tr > th, +.table-condensed > tfoot > tr > th, +.table-condensed > thead > tr > td, +.table-condensed > tbody > tr > td, +.table-condensed > tfoot > tr > td { + padding: 5px; +} +.table-bordered { + border: 1px solid #ddd; +} +.table-bordered > thead > tr > th, +.table-bordered > tbody > tr > th, +.table-bordered > tfoot > tr > th, +.table-bordered > thead > tr > td, +.table-bordered > tbody > tr > td, +.table-bordered > tfoot > tr > td { + border: 1px solid #ddd; +} +.table-bordered > thead > tr > th, +.table-bordered > thead > tr > td { + border-bottom-width: 2px; +} +.table-striped > tbody > tr:nth-child(odd) > td, +.table-striped > tbody > tr:nth-child(odd) > th { + background-color: #f9f9f9; +} +.table-hover > tbody > tr:hover > td, +.table-hover > tbody > tr:hover > th { + background-color: #f5f5f5; +} +table col[class*="col-"] { + position: static; + display: table-column; + float: none; +} +table td[class*="col-"], +table th[class*="col-"] { + position: static; + display: table-cell; + float: none; +} +.table > thead > tr > td.active, +.table > tbody > tr > td.active, +.table > tfoot > tr > td.active, +.table > thead > tr > th.active, +.table > tbody > tr > th.active, +.table > tfoot > tr > th.active, +.table > thead > tr.active > td, +.table > tbody > tr.active > td, +.table > tfoot > tr.active > td, +.table > thead > tr.active > th, +.table > tbody > tr.active > th, +.table > tfoot > tr.active > th { + background-color: #f5f5f5; +} +.table-hover > tbody > tr > td.active:hover, +.table-hover > tbody > tr > th.active:hover, +.table-hover > tbody > tr.active:hover > td, +.table-hover > tbody > tr:hover > .active, +.table-hover > tbody > tr.active:hover > th { + background-color: #e8e8e8; +} +.table > thead > tr > td.success, +.table > tbody > tr > td.success, +.table > tfoot > tr > td.success, +.table > thead > tr > th.success, +.table > tbody > tr > th.success, +.table > tfoot > tr > th.success, +.table > thead > tr.success > td, +.table > tbody > tr.success > td, +.table > tfoot > tr.success > td, +.table > thead > tr.success > th, +.table > tbody > tr.success > th, +.table > tfoot > tr.success > th { + background-color: #dff0d8; +} +.table-hover > tbody > tr > td.success:hover, +.table-hover > tbody > tr > th.success:hover, +.table-hover > tbody > tr.success:hover > td, +.table-hover > tbody > tr:hover > .success, +.table-hover > tbody > tr.success:hover > th { + background-color: #d0e9c6; +} +.table > thead > tr > td.info, +.table > tbody > tr > td.info, +.table > tfoot > tr > td.info, +.table > thead > tr > th.info, +.table > tbody > tr > th.info, +.table > tfoot > tr > th.info, +.table > thead > tr.info > td, +.table > tbody > tr.info > td, +.table > tfoot > tr.info > td, +.table > thead > tr.info > th, +.table > tbody > tr.info > th, +.table > tfoot > tr.info > th { + background-color: #d9edf7; +} +.table-hover > tbody > tr > td.info:hover, +.table-hover > tbody > tr > th.info:hover, +.table-hover > tbody > tr.info:hover > td, +.table-hover > tbody > tr:hover > .info, +.table-hover > tbody > tr.info:hover > th { + background-color: #c4e3f3; +} +.table > thead > tr > td.warning, +.table > tbody > tr > td.warning, +.table > tfoot > tr > td.warning, +.table > thead > tr > th.warning, +.table > tbody > tr > th.warning, +.table > tfoot > tr > th.warning, +.table > thead > tr.warning > td, +.table > tbody > tr.warning > td, +.table > tfoot > tr.warning > td, +.table > thead > tr.warning > th, +.table > tbody > tr.warning > th, +.table > tfoot > tr.warning > th { + background-color: #fcf8e3; +} +.table-hover > tbody > tr > td.warning:hover, +.table-hover > tbody > tr > th.warning:hover, +.table-hover > tbody > tr.warning:hover > td, +.table-hover > tbody > tr:hover > .warning, +.table-hover > tbody > tr.warning:hover > th { + background-color: #faf2cc; +} +.table > thead > tr > td.danger, +.table > tbody > tr > td.danger, +.table > tfoot > tr > td.danger, +.table > thead > tr > th.danger, +.table > tbody > tr > th.danger, +.table > tfoot > tr > th.danger, +.table > thead > tr.danger > td, +.table > tbody > tr.danger > td, +.table > tfoot > tr.danger > td, +.table > thead > tr.danger > th, +.table > tbody > tr.danger > th, +.table > tfoot > tr.danger > th { + background-color: #f2dede; +} +.table-hover > tbody > tr > td.danger:hover, +.table-hover > tbody > tr > th.danger:hover, +.table-hover > tbody > tr.danger:hover > td, +.table-hover > tbody > tr:hover > .danger, +.table-hover > tbody > tr.danger:hover > th { + background-color: #ebcccc; +} +@media screen and (max-width: 767px) { + .table-responsive { + width: 100%; + margin-bottom: 15px; + overflow-x: auto; + overflow-y: hidden; + -webkit-overflow-scrolling: touch; + -ms-overflow-style: -ms-autohiding-scrollbar; + border: 1px solid #ddd; + } + .table-responsive > .table { + margin-bottom: 0; + } + .table-responsive > .table > thead > tr > th, + .table-responsive > .table > tbody > tr > th, + .table-responsive > .table > tfoot > tr > th, + .table-responsive > .table > thead > tr > td, + .table-responsive > .table > tbody > tr > td, + .table-responsive > .table > tfoot > tr > td { + white-space: nowrap; + } + .table-responsive > .table-bordered { + border: 0; + } + .table-responsive > .table-bordered > thead > tr > th:first-child, + .table-responsive > .table-bordered > tbody > tr > th:first-child, + .table-responsive > .table-bordered > tfoot > tr > th:first-child, + .table-responsive > .table-bordered > thead > tr > td:first-child, + .table-responsive > .table-bordered > tbody > tr > td:first-child, + .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; + } + .table-responsive > .table-bordered > thead > tr > th:last-child, + .table-responsive > .table-bordered > tbody > tr > th:last-child, + .table-responsive > .table-bordered > tfoot > tr > th:last-child, + .table-responsive > .table-bordered > thead > tr > td:last-child, + .table-responsive > .table-bordered > tbody > tr > td:last-child, + .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; + } + .table-responsive > .table-bordered > tbody > tr:last-child > th, + .table-responsive > .table-bordered > tfoot > tr:last-child > th, + .table-responsive > .table-bordered > tbody > tr:last-child > td, + .table-responsive > .table-bordered > tfoot > tr:last-child > td { + border-bottom: 0; + } +} +fieldset { + min-width: 0; + padding: 0; + margin: 0; + border: 0; +} +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: 20px; + font-size: 21px; + line-height: inherit; + color: #333; + border: 0; + border-bottom: 1px solid #e5e5e5; +} +label { + display: inline-block; + max-width: 100%; + margin-bottom: 5px; + font-weight: bold; +} +input[type="search"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +input[type="radio"], +input[type="checkbox"] { + margin: 4px 0 0; + margin-top: 1px \9; + line-height: normal; +} +input[type="file"] { + display: block; +} +input[type="range"] { + display: block; + width: 100%; +} +select[multiple], +select[size] { + height: auto; +} +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +output { + display: block; + padding-top: 7px; + font-size: 14px; + line-height: 1.42857143; + color: #555; +} +.form-control { + display: block; + width: 100%; + height: 34px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.42857143; + color: #555; + background-color: #fff; + background-image: none; + border: 1px solid #ccc; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; + -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; +} +.form-control:focus { + border-color: #66afe9; + outline: 0; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); +} +.form-control::-moz-placeholder { + color: #777; + opacity: 1; +} +.form-control:-ms-input-placeholder { + color: #777; +} +.form-control::-webkit-input-placeholder { + color: #777; +} +.form-control[disabled], +.form-control[readonly], +fieldset[disabled] .form-control { + cursor: not-allowed; + background-color: #eee; + opacity: 1; +} +textarea.form-control { + height: auto; +} +input[type="search"] { + -webkit-appearance: none; +} +input[type="date"], +input[type="time"], +input[type="datetime-local"], +input[type="month"] { + line-height: 34px; + line-height: 1.42857143 \0; +} +input[type="date"].input-sm, +input[type="time"].input-sm, +input[type="datetime-local"].input-sm, +input[type="month"].input-sm { + line-height: 30px; +} +input[type="date"].input-lg, +input[type="time"].input-lg, +input[type="datetime-local"].input-lg, +input[type="month"].input-lg { + line-height: 46px; +} +.form-group { + margin-bottom: 15px; +} +.radio, +.checkbox { + position: relative; + display: block; + min-height: 20px; + margin-top: 10px; + margin-bottom: 10px; +} +.radio label, +.checkbox label { + padding-left: 20px; + margin-bottom: 0; + font-weight: normal; + cursor: pointer; +} +.radio input[type="radio"], +.radio-inline input[type="radio"], +.checkbox input[type="checkbox"], +.checkbox-inline input[type="checkbox"] { + position: absolute; + margin-top: 4px \9; + margin-left: -20px; +} +.radio + .radio, +.checkbox + .checkbox { + margin-top: -5px; +} +.radio-inline, +.checkbox-inline { + display: inline-block; + padding-left: 20px; + margin-bottom: 0; + font-weight: normal; + vertical-align: middle; + cursor: pointer; +} +.radio-inline + .radio-inline, +.checkbox-inline + .checkbox-inline { + margin-top: 0; + margin-left: 10px; +} +input[type="radio"][disabled], +input[type="checkbox"][disabled], +input[type="radio"].disabled, +input[type="checkbox"].disabled, +fieldset[disabled] input[type="radio"], +fieldset[disabled] input[type="checkbox"] { + cursor: not-allowed; +} +.radio-inline.disabled, +.checkbox-inline.disabled, +fieldset[disabled] .radio-inline, +fieldset[disabled] .checkbox-inline { + cursor: not-allowed; +} +.radio.disabled label, +.checkbox.disabled label, +fieldset[disabled] .radio label, +fieldset[disabled] .checkbox label { + cursor: not-allowed; +} +.form-control-static { + padding-top: 7px; + padding-bottom: 7px; + margin-bottom: 0; +} +.form-control-static.input-lg, +.form-control-static.input-sm { + padding-right: 0; + padding-left: 0; +} +.input-sm, +.form-horizontal .form-group-sm .form-control { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.input-sm { + height: 30px; + line-height: 30px; +} +textarea.input-sm, +select[multiple].input-sm { + height: auto; +} +.input-lg, +.form-horizontal .form-group-lg .form-control { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.33; + border-radius: 6px; +} +select.input-lg { + height: 46px; + line-height: 46px; +} +textarea.input-lg, +select[multiple].input-lg { + height: auto; +} +.has-feedback { + position: relative; +} +.has-feedback .form-control { + padding-right: 42.5px; +} +.form-control-feedback { + position: absolute; + top: 25px; + right: 0; + z-index: 2; + display: block; + width: 34px; + height: 34px; + line-height: 34px; + text-align: center; +} +.input-lg + .form-control-feedback { + width: 46px; + height: 46px; + line-height: 46px; +} +.input-sm + .form-control-feedback { + width: 30px; + height: 30px; + line-height: 30px; +} +.has-success .help-block, +.has-success .control-label, +.has-success .radio, +.has-success .checkbox, +.has-success .radio-inline, +.has-success .checkbox-inline { + color: #3c763d; +} +.has-success .form-control { + border-color: #3c763d; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} +.has-success .form-control:focus { + border-color: #2b542c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; +} +.has-success .input-group-addon { + color: #3c763d; + background-color: #dff0d8; + border-color: #3c763d; +} +.has-success .form-control-feedback { + color: #3c763d; +} +.has-warning .help-block, +.has-warning .control-label, +.has-warning .radio, +.has-warning .checkbox, +.has-warning .radio-inline, +.has-warning .checkbox-inline { + color: #8a6d3b; +} +.has-warning .form-control { + border-color: #8a6d3b; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} +.has-warning .form-control:focus { + border-color: #66512c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; +} +.has-warning .input-group-addon { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #8a6d3b; +} +.has-warning .form-control-feedback { + color: #8a6d3b; +} +.has-error .help-block, +.has-error .control-label, +.has-error .radio, +.has-error .checkbox, +.has-error .radio-inline, +.has-error .checkbox-inline { + color: #a94442; +} +.has-error .form-control { + border-color: #a94442; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} +.has-error .form-control:focus { + border-color: #843534; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; +} +.has-error .input-group-addon { + color: #a94442; + background-color: #f2dede; + border-color: #a94442; +} +.has-error .form-control-feedback { + color: #a94442; +} +.has-feedback label.sr-only ~ .form-control-feedback { + top: 0; +} +.help-block { + display: block; + margin-top: 5px; + margin-bottom: 10px; + color: #737373; +} +@media (min-width: 768px) { + .form-inline .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .form-inline .input-group { + display: inline-table; + vertical-align: middle; + } + .form-inline .input-group .input-group-addon, + .form-inline .input-group .input-group-btn, + .form-inline .input-group .form-control { + width: auto; + } + .form-inline .input-group > .form-control { + width: 100%; + } + .form-inline .control-label { + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .radio, + .form-inline .checkbox { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .radio label, + .form-inline .checkbox label { + padding-left: 0; + } + .form-inline .radio input[type="radio"], + .form-inline .checkbox input[type="checkbox"] { + position: relative; + margin-left: 0; + } + .form-inline .has-feedback .form-control-feedback { + top: 0; + } +} +.form-horizontal .radio, +.form-horizontal .checkbox, +.form-horizontal .radio-inline, +.form-horizontal .checkbox-inline { + padding-top: 7px; + margin-top: 0; + margin-bottom: 0; +} +.form-horizontal .radio, +.form-horizontal .checkbox { + min-height: 27px; +} +.form-horizontal .form-group { + margin-right: -15px; + margin-left: -15px; +} +@media (min-width: 768px) { + .form-horizontal .control-label { + padding-top: 7px; + margin-bottom: 0; + text-align: right; + } +} +.form-horizontal .has-feedback .form-control-feedback { + top: 0; + right: 15px; +} +@media (min-width: 768px) { + .form-horizontal .form-group-lg .control-label { + padding-top: 14.3px; + } +} +@media (min-width: 768px) { + .form-horizontal .form-group-sm .control-label { + padding-top: 6px; + } +} +.btn { + display: inline-block; + padding: 6px 12px; + margin-bottom: 0; + font-size: 14px; + font-weight: normal; + line-height: 1.42857143; + text-align: center; + white-space: nowrap; + vertical-align: middle; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background-image: none; + border: 1px solid transparent; + border-radius: 4px; +} +.btn:focus, +.btn:active:focus, +.btn.active:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.btn:hover, +.btn:focus { + color: #333; + text-decoration: none; +} +.btn:active, +.btn.active { + background-image: none; + outline: 0; + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); +} +.btn.disabled, +.btn[disabled], +fieldset[disabled] .btn { + pointer-events: none; + cursor: not-allowed; + filter: alpha(opacity=65); + -webkit-box-shadow: none; + box-shadow: none; + opacity: .65; +} +.btn-default { + color: #333; + background-color: #fff; + border-color: #ccc; +} +.btn-default:hover, +.btn-default:focus, +.btn-default:active, +.btn-default.active, +.open > .dropdown-toggle.btn-default { + color: #333; + background-color: #e6e6e6; + border-color: #adadad; +} +.btn-default:active, +.btn-default.active, +.open > .dropdown-toggle.btn-default { + background-image: none; +} +.btn-default.disabled, +.btn-default[disabled], +fieldset[disabled] .btn-default, +.btn-default.disabled:hover, +.btn-default[disabled]:hover, +fieldset[disabled] .btn-default:hover, +.btn-default.disabled:focus, +.btn-default[disabled]:focus, +fieldset[disabled] .btn-default:focus, +.btn-default.disabled:active, +.btn-default[disabled]:active, +fieldset[disabled] .btn-default:active, +.btn-default.disabled.active, +.btn-default[disabled].active, +fieldset[disabled] .btn-default.active { + background-color: #fff; + border-color: #ccc; +} +.btn-default .badge { + color: #fff; + background-color: #333; +} +.btn-primary { + color: #fff; + background-color: #428bca; + border-color: #357ebd; +} +.btn-primary:hover, +.btn-primary:focus, +.btn-primary:active, +.btn-primary.active, +.open > .dropdown-toggle.btn-primary { + color: #fff; + background-color: #3071a9; + border-color: #285e8e; +} +.btn-primary:active, +.btn-primary.active, +.open > .dropdown-toggle.btn-primary { + background-image: none; +} +.btn-primary.disabled, +.btn-primary[disabled], +fieldset[disabled] .btn-primary, +.btn-primary.disabled:hover, +.btn-primary[disabled]:hover, +fieldset[disabled] .btn-primary:hover, +.btn-primary.disabled:focus, +.btn-primary[disabled]:focus, +fieldset[disabled] .btn-primary:focus, +.btn-primary.disabled:active, +.btn-primary[disabled]:active, +fieldset[disabled] .btn-primary:active, +.btn-primary.disabled.active, +.btn-primary[disabled].active, +fieldset[disabled] .btn-primary.active { + background-color: #428bca; + border-color: #357ebd; +} +.btn-primary .badge { + color: #428bca; + background-color: #fff; +} +.btn-success { + color: #fff; + background-color: #5cb85c; + border-color: #4cae4c; +} +.btn-success:hover, +.btn-success:focus, +.btn-success:active, +.btn-success.active, +.open > .dropdown-toggle.btn-success { + color: #fff; + background-color: #449d44; + border-color: #398439; +} +.btn-success:active, +.btn-success.active, +.open > .dropdown-toggle.btn-success { + background-image: none; +} +.btn-success.disabled, +.btn-success[disabled], +fieldset[disabled] .btn-success, +.btn-success.disabled:hover, +.btn-success[disabled]:hover, +fieldset[disabled] .btn-success:hover, +.btn-success.disabled:focus, +.btn-success[disabled]:focus, +fieldset[disabled] .btn-success:focus, +.btn-success.disabled:active, +.btn-success[disabled]:active, +fieldset[disabled] .btn-success:active, +.btn-success.disabled.active, +.btn-success[disabled].active, +fieldset[disabled] .btn-success.active { + background-color: #5cb85c; + border-color: #4cae4c; +} +.btn-success .badge { + color: #5cb85c; + background-color: #fff; +} +.btn-info { + color: #fff; + background-color: #5bc0de; + border-color: #46b8da; +} +.btn-info:hover, +.btn-info:focus, +.btn-info:active, +.btn-info.active, +.open > .dropdown-toggle.btn-info { + color: #fff; + background-color: #31b0d5; + border-color: #269abc; +} +.btn-info:active, +.btn-info.active, +.open > .dropdown-toggle.btn-info { + background-image: none; +} +.btn-info.disabled, +.btn-info[disabled], +fieldset[disabled] .btn-info, +.btn-info.disabled:hover, +.btn-info[disabled]:hover, +fieldset[disabled] .btn-info:hover, +.btn-info.disabled:focus, +.btn-info[disabled]:focus, +fieldset[disabled] .btn-info:focus, +.btn-info.disabled:active, +.btn-info[disabled]:active, +fieldset[disabled] .btn-info:active, +.btn-info.disabled.active, +.btn-info[disabled].active, +fieldset[disabled] .btn-info.active { + background-color: #5bc0de; + border-color: #46b8da; +} +.btn-info .badge { + color: #5bc0de; + background-color: #fff; +} +.btn-warning { + color: #fff; + background-color: #f0ad4e; + border-color: #eea236; +} +.btn-warning:hover, +.btn-warning:focus, +.btn-warning:active, +.btn-warning.active, +.open > .dropdown-toggle.btn-warning { + color: #fff; + background-color: #ec971f; + border-color: #d58512; +} +.btn-warning:active, +.btn-warning.active, +.open > .dropdown-toggle.btn-warning { + background-image: none; +} +.btn-warning.disabled, +.btn-warning[disabled], +fieldset[disabled] .btn-warning, +.btn-warning.disabled:hover, +.btn-warning[disabled]:hover, +fieldset[disabled] .btn-warning:hover, +.btn-warning.disabled:focus, +.btn-warning[disabled]:focus, +fieldset[disabled] .btn-warning:focus, +.btn-warning.disabled:active, +.btn-warning[disabled]:active, +fieldset[disabled] .btn-warning:active, +.btn-warning.disabled.active, +.btn-warning[disabled].active, +fieldset[disabled] .btn-warning.active { + background-color: #f0ad4e; + border-color: #eea236; +} +.btn-warning .badge { + color: #f0ad4e; + background-color: #fff; +} +.btn-danger { + color: #fff; + background-color: #d9534f; + border-color: #d43f3a; +} +.btn-danger:hover, +.btn-danger:focus, +.btn-danger:active, +.btn-danger.active, +.open > .dropdown-toggle.btn-danger { + color: #fff; + background-color: #c9302c; + border-color: #ac2925; +} +.btn-danger:active, +.btn-danger.active, +.open > .dropdown-toggle.btn-danger { + background-image: none; +} +.btn-danger.disabled, +.btn-danger[disabled], +fieldset[disabled] .btn-danger, +.btn-danger.disabled:hover, +.btn-danger[disabled]:hover, +fieldset[disabled] .btn-danger:hover, +.btn-danger.disabled:focus, +.btn-danger[disabled]:focus, +fieldset[disabled] .btn-danger:focus, +.btn-danger.disabled:active, +.btn-danger[disabled]:active, +fieldset[disabled] .btn-danger:active, +.btn-danger.disabled.active, +.btn-danger[disabled].active, +fieldset[disabled] .btn-danger.active { + background-color: #d9534f; + border-color: #d43f3a; +} +.btn-danger .badge { + color: #d9534f; + background-color: #fff; +} +.btn-link { + font-weight: normal; + color: #428bca; + cursor: pointer; + border-radius: 0; +} +.btn-link, +.btn-link:active, +.btn-link[disabled], +fieldset[disabled] .btn-link { + background-color: transparent; + -webkit-box-shadow: none; + box-shadow: none; +} +.btn-link, +.btn-link:hover, +.btn-link:focus, +.btn-link:active { + border-color: transparent; +} +.btn-link:hover, +.btn-link:focus { + color: #2a6496; + text-decoration: underline; + background-color: transparent; +} +.btn-link[disabled]:hover, +fieldset[disabled] .btn-link:hover, +.btn-link[disabled]:focus, +fieldset[disabled] .btn-link:focus { + color: #777; + text-decoration: none; +} +.btn-lg, +.btn-group-lg > .btn { + padding: 10px 16px; + font-size: 18px; + line-height: 1.33; + border-radius: 6px; +} +.btn-sm, +.btn-group-sm > .btn { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.btn-xs, +.btn-group-xs > .btn { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.btn-block { + display: block; + width: 100%; +} +.btn-block + .btn-block { + margin-top: 5px; +} +input[type="submit"].btn-block, +input[type="reset"].btn-block, +input[type="button"].btn-block { + width: 100%; +} +.fade { + opacity: 0; + -webkit-transition: opacity .15s linear; + -o-transition: opacity .15s linear; + transition: opacity .15s linear; +} +.fade.in { + opacity: 1; +} +.collapse { + display: none; +} +.collapse.in { + display: block; +} +tr.collapse.in { + display: table-row; +} +tbody.collapse.in { + display: table-row-group; +} +.collapsing { + position: relative; + height: 0; + overflow: hidden; + -webkit-transition: height .35s ease; + -o-transition: height .35s ease; + transition: height .35s ease; +} +.caret { + display: inline-block; + width: 0; + height: 0; + margin-left: 2px; + vertical-align: middle; + border-top: 4px solid; + border-right: 4px solid transparent; + border-left: 4px solid transparent; +} +.dropdown { + position: relative; +} +.dropdown-toggle:focus { + outline: 0; +} +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; + font-size: 14px; + text-align: left; + list-style: none; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, .15); + border-radius: 4px; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); + box-shadow: 0 6px 12px rgba(0, 0, 0, .175); +} +.dropdown-menu.pull-right { + right: 0; + left: auto; +} +.dropdown-menu .divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.dropdown-menu > li > a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: 1.42857143; + color: #333; + white-space: nowrap; +} +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus { + color: #262626; + text-decoration: none; + background-color: #f5f5f5; +} +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + color: #fff; + text-decoration: none; + background-color: #428bca; + outline: 0; +} +.dropdown-menu > .disabled > a, +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + color: #777; +} +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + text-decoration: none; + cursor: not-allowed; + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} +.open > .dropdown-menu { + display: block; +} +.open > a { + outline: 0; +} +.dropdown-menu-right { + right: 0; + left: auto; +} +.dropdown-menu-left { + right: auto; + left: 0; +} +.dropdown-header { + display: block; + padding: 3px 20px; + font-size: 12px; + line-height: 1.42857143; + color: #777; + white-space: nowrap; +} +.dropdown-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 990; +} +.pull-right > .dropdown-menu { + right: 0; + left: auto; +} +.dropup .caret, +.navbar-fixed-bottom .dropdown .caret { + content: ""; + border-top: 0; + border-bottom: 4px solid; +} +.dropup .dropdown-menu, +.navbar-fixed-bottom .dropdown .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 1px; +} +@media (min-width: 768px) { + .navbar-right .dropdown-menu { + right: 0; + left: auto; + } + .navbar-right .dropdown-menu-left { + right: auto; + left: 0; + } +} +.btn-group, +.btn-group-vertical { + position: relative; + display: inline-block; + vertical-align: middle; +} +.btn-group > .btn, +.btn-group-vertical > .btn { + position: relative; + float: left; +} +.btn-group > .btn:hover, +.btn-group-vertical > .btn:hover, +.btn-group > .btn:focus, +.btn-group-vertical > .btn:focus, +.btn-group > .btn:active, +.btn-group-vertical > .btn:active, +.btn-group > .btn.active, +.btn-group-vertical > .btn.active { + z-index: 2; +} +.btn-group > .btn:focus, +.btn-group-vertical > .btn:focus { + outline: 0; +} +.btn-group .btn + .btn, +.btn-group .btn + .btn-group, +.btn-group .btn-group + .btn, +.btn-group .btn-group + .btn-group { + margin-left: -1px; +} +.btn-toolbar { + margin-left: -5px; +} +.btn-toolbar .btn-group, +.btn-toolbar .input-group { + float: left; +} +.btn-toolbar > .btn, +.btn-toolbar > .btn-group, +.btn-toolbar > .input-group { + margin-left: 5px; +} +.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { + border-radius: 0; +} +.btn-group > .btn:first-child { + margin-left: 0; +} +.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.btn-group > .btn:last-child:not(:first-child), +.btn-group > .dropdown-toggle:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group > .btn-group { + float: left; +} +.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group > .btn-group:first-child > .btn:last-child, +.btn-group > .btn-group:first-child > .dropdown-toggle { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.btn-group > .btn-group:last-child > .btn:first-child { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group .dropdown-toggle:active, +.btn-group.open .dropdown-toggle { + outline: 0; +} +.btn-group > .btn + .dropdown-toggle { + padding-right: 8px; + padding-left: 8px; +} +.btn-group > .btn-lg + .dropdown-toggle { + padding-right: 12px; + padding-left: 12px; +} +.btn-group.open .dropdown-toggle { + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); +} +.btn-group.open .dropdown-toggle.btn-link { + -webkit-box-shadow: none; + box-shadow: none; +} +.btn .caret { + margin-left: 0; +} +.btn-lg .caret { + border-width: 5px 5px 0; + border-bottom-width: 0; +} +.dropup .btn-lg .caret { + border-width: 0 5px 5px; +} +.btn-group-vertical > .btn, +.btn-group-vertical > .btn-group, +.btn-group-vertical > .btn-group > .btn { + display: block; + float: none; + width: 100%; + max-width: 100%; +} +.btn-group-vertical > .btn-group > .btn { + float: none; +} +.btn-group-vertical > .btn + .btn, +.btn-group-vertical > .btn + .btn-group, +.btn-group-vertical > .btn-group + .btn, +.btn-group-vertical > .btn-group + .btn-group { + margin-top: -1px; + margin-left: 0; +} +.btn-group-vertical > .btn:not(:first-child):not(:last-child) { + border-radius: 0; +} +.btn-group-vertical > .btn:first-child:not(:last-child) { + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group-vertical > .btn:last-child:not(:first-child) { + border-top-left-radius: 0; + border-top-right-radius: 0; + border-bottom-left-radius: 4px; +} +.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child, +.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.btn-group-justified { + display: table; + width: 100%; + table-layout: fixed; + border-collapse: separate; +} +.btn-group-justified > .btn, +.btn-group-justified > .btn-group { + display: table-cell; + float: none; + width: 1%; +} +.btn-group-justified > .btn-group .btn { + width: 100%; +} +.btn-group-justified > .btn-group .dropdown-menu { + left: auto; +} +[data-toggle="buttons"] > .btn > input[type="radio"], +[data-toggle="buttons"] > .btn > input[type="checkbox"] { + position: absolute; + z-index: -1; + filter: alpha(opacity=0); + opacity: 0; +} +.input-group { + position: relative; + display: table; + border-collapse: separate; +} +.input-group[class*="col-"] { + float: none; + padding-right: 0; + padding-left: 0; +} +.input-group .form-control { + position: relative; + z-index: 2; + float: left; + width: 100%; + margin-bottom: 0; +} +.input-group-lg > .form-control, +.input-group-lg > .input-group-addon, +.input-group-lg > .input-group-btn > .btn { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.33; + border-radius: 6px; +} +select.input-group-lg > .form-control, +select.input-group-lg > .input-group-addon, +select.input-group-lg > .input-group-btn > .btn { + height: 46px; + line-height: 46px; +} +textarea.input-group-lg > .form-control, +textarea.input-group-lg > .input-group-addon, +textarea.input-group-lg > .input-group-btn > .btn, +select[multiple].input-group-lg > .form-control, +select[multiple].input-group-lg > .input-group-addon, +select[multiple].input-group-lg > .input-group-btn > .btn { + height: auto; +} +.input-group-sm > .form-control, +.input-group-sm > .input-group-addon, +.input-group-sm > .input-group-btn > .btn { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.input-group-sm > .form-control, +select.input-group-sm > .input-group-addon, +select.input-group-sm > .input-group-btn > .btn { + height: 30px; + line-height: 30px; +} +textarea.input-group-sm > .form-control, +textarea.input-group-sm > .input-group-addon, +textarea.input-group-sm > .input-group-btn > .btn, +select[multiple].input-group-sm > .form-control, +select[multiple].input-group-sm > .input-group-addon, +select[multiple].input-group-sm > .input-group-btn > .btn { + height: auto; +} +.input-group-addon, +.input-group-btn, +.input-group .form-control { + display: table-cell; +} +.input-group-addon:not(:first-child):not(:last-child), +.input-group-btn:not(:first-child):not(:last-child), +.input-group .form-control:not(:first-child):not(:last-child) { + border-radius: 0; +} +.input-group-addon, +.input-group-btn { + width: 1%; + white-space: nowrap; + vertical-align: middle; +} +.input-group-addon { + padding: 6px 12px; + font-size: 14px; + font-weight: normal; + line-height: 1; + color: #555; + text-align: center; + background-color: #eee; + border: 1px solid #ccc; + border-radius: 4px; +} +.input-group-addon.input-sm { + padding: 5px 10px; + font-size: 12px; + border-radius: 3px; +} +.input-group-addon.input-lg { + padding: 10px 16px; + font-size: 18px; + border-radius: 6px; +} +.input-group-addon input[type="radio"], +.input-group-addon input[type="checkbox"] { + margin-top: 0; +} +.input-group .form-control:first-child, +.input-group-addon:first-child, +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group > .btn, +.input-group-btn:first-child > .dropdown-toggle, +.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), +.input-group-btn:last-child > .btn-group:not(:last-child) > .btn { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.input-group-addon:first-child { + border-right: 0; +} +.input-group .form-control:last-child, +.input-group-addon:last-child, +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group > .btn, +.input-group-btn:last-child > .dropdown-toggle, +.input-group-btn:first-child > .btn:not(:first-child), +.input-group-btn:first-child > .btn-group:not(:first-child) > .btn { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.input-group-addon:last-child { + border-left: 0; +} +.input-group-btn { + position: relative; + font-size: 0; + white-space: nowrap; +} +.input-group-btn > .btn { + position: relative; +} +.input-group-btn > .btn + .btn { + margin-left: -1px; +} +.input-group-btn > .btn:hover, +.input-group-btn > .btn:focus, +.input-group-btn > .btn:active { + z-index: 2; +} +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group { + margin-right: -1px; +} +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group { + margin-left: -1px; +} +.nav { + padding-left: 0; + margin-bottom: 0; + list-style: none; +} +.nav > li { + position: relative; + display: block; +} +.nav > li > a { + position: relative; + display: block; + padding: 10px 15px; +} +.nav > li > a:hover, +.nav > li > a:focus { + text-decoration: none; + background-color: #eee; +} +.nav > li.disabled > a { + color: #777; +} +.nav > li.disabled > a:hover, +.nav > li.disabled > a:focus { + color: #777; + text-decoration: none; + cursor: not-allowed; + background-color: transparent; +} +.nav .open > a, +.nav .open > a:hover, +.nav .open > a:focus { + background-color: #eee; + border-color: #428bca; +} +.nav .nav-divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.nav > li > a > img { + max-width: none; +} +.nav-tabs { + border-bottom: 1px solid #ddd; +} +.nav-tabs > li { + float: left; + margin-bottom: -1px; +} +.nav-tabs > li > a { + margin-right: 2px; + line-height: 1.42857143; + border: 1px solid transparent; + border-radius: 4px 4px 0 0; +} +.nav-tabs > li > a:hover { + border-color: #eee #eee #ddd; +} +.nav-tabs > li.active > a, +.nav-tabs > li.active > a:hover, +.nav-tabs > li.active > a:focus { + color: #555; + cursor: default; + background-color: #fff; + border: 1px solid #ddd; + border-bottom-color: transparent; +} +.nav-tabs.nav-justified { + width: 100%; + border-bottom: 0; +} +.nav-tabs.nav-justified > li { + float: none; +} +.nav-tabs.nav-justified > li > a { + margin-bottom: 5px; + text-align: center; +} +.nav-tabs.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-tabs.nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs.nav-justified > li > a { + margin-right: 0; + border-radius: 4px; +} +.nav-tabs.nav-justified > .active > a, +.nav-tabs.nav-justified > .active > a:hover, +.nav-tabs.nav-justified > .active > a:focus { + border: 1px solid #ddd; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li > a { + border-bottom: 1px solid #ddd; + border-radius: 4px 4px 0 0; + } + .nav-tabs.nav-justified > .active > a, + .nav-tabs.nav-justified > .active > a:hover, + .nav-tabs.nav-justified > .active > a:focus { + border-bottom-color: #fff; + } +} +.nav-pills > li { + float: left; +} +.nav-pills > li > a { + border-radius: 4px; +} +.nav-pills > li + li { + margin-left: 2px; +} +.nav-pills > li.active > a, +.nav-pills > li.active > a:hover, +.nav-pills > li.active > a:focus { + color: #fff; + background-color: #428bca; +} +.nav-stacked > li { + float: none; +} +.nav-stacked > li + li { + margin-top: 2px; + margin-left: 0; +} +.nav-justified { + width: 100%; +} +.nav-justified > li { + float: none; +} +.nav-justified > li > a { + margin-bottom: 5px; + text-align: center; +} +.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs-justified { + border-bottom: 0; +} +.nav-tabs-justified > li > a { + margin-right: 0; + border-radius: 4px; +} +.nav-tabs-justified > .active > a, +.nav-tabs-justified > .active > a:hover, +.nav-tabs-justified > .active > a:focus { + border: 1px solid #ddd; +} +@media (min-width: 768px) { + .nav-tabs-justified > li > a { + border-bottom: 1px solid #ddd; + border-radius: 4px 4px 0 0; + } + .nav-tabs-justified > .active > a, + .nav-tabs-justified > .active > a:hover, + .nav-tabs-justified > .active > a:focus { + border-bottom-color: #fff; + } +} +.tab-content > .tab-pane { + display: none; +} +.tab-content > .active { + display: block; +} +.nav-tabs .dropdown-menu { + margin-top: -1px; + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.navbar { + position: relative; + min-height: 50px; + margin-bottom: 20px; + border: 1px solid transparent; +} +@media (min-width: 768px) { + .navbar { + border-radius: 4px; + } +} +@media (min-width: 768px) { + .navbar-header { + float: left; + } +} +.navbar-collapse { + padding-right: 15px; + padding-left: 15px; + overflow-x: visible; + -webkit-overflow-scrolling: touch; + border-top: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); +} +.navbar-collapse.in { + overflow-y: auto; +} +@media (min-width: 768px) { + .navbar-collapse { + width: auto; + border-top: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-collapse.collapse { + display: block !important; + height: auto !important; + padding-bottom: 0; + overflow: visible !important; + } + .navbar-collapse.in { + overflow-y: visible; + } + .navbar-fixed-top .navbar-collapse, + .navbar-static-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + padding-right: 0; + padding-left: 0; + } +} +.navbar-fixed-top .navbar-collapse, +.navbar-fixed-bottom .navbar-collapse { + max-height: 340px; +} +@media (max-width: 480px) and (orientation: landscape) { + .navbar-fixed-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + max-height: 200px; + } +} +.container > .navbar-header, +.container-fluid > .navbar-header, +.container > .navbar-collapse, +.container-fluid > .navbar-collapse { + margin-right: -15px; + margin-left: -15px; +} +@media (min-width: 768px) { + .container > .navbar-header, + .container-fluid > .navbar-header, + .container > .navbar-collapse, + .container-fluid > .navbar-collapse { + margin-right: 0; + margin-left: 0; + } +} +.navbar-static-top { + z-index: 1000; + border-width: 0 0 1px; +} +@media (min-width: 768px) { + .navbar-static-top { + border-radius: 0; + } +} +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030; + -webkit-transform: translate3d(0, 0, 0); + -o-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); +} +@media (min-width: 768px) { + .navbar-fixed-top, + .navbar-fixed-bottom { + border-radius: 0; + } +} +.navbar-fixed-top { + top: 0; + border-width: 0 0 1px; +} +.navbar-fixed-bottom { + bottom: 0; + margin-bottom: 0; + border-width: 1px 0 0; +} +.navbar-brand { + float: left; + height: 50px; + padding: 15px 15px; + font-size: 18px; + line-height: 20px; +} +.navbar-brand:hover, +.navbar-brand:focus { + text-decoration: none; +} +@media (min-width: 768px) { + .navbar > .container .navbar-brand, + .navbar > .container-fluid .navbar-brand { + margin-left: -15px; + } +} +.navbar-toggle { + position: relative; + float: right; + padding: 9px 10px; + margin-top: 8px; + margin-right: 15px; + margin-bottom: 8px; + background-color: transparent; + background-image: none; + border: 1px solid transparent; + border-radius: 4px; +} +.navbar-toggle:focus { + outline: 0; +} +.navbar-toggle .icon-bar { + display: block; + width: 22px; + height: 2px; + border-radius: 1px; +} +.navbar-toggle .icon-bar + .icon-bar { + margin-top: 4px; +} +@media (min-width: 768px) { + .navbar-toggle { + display: none; + } +} +.navbar-nav { + margin: 7.5px -15px; +} +.navbar-nav > li > a { + padding-top: 10px; + padding-bottom: 10px; + line-height: 20px; +} +@media (max-width: 767px) { + .navbar-nav .open .dropdown-menu { + position: static; + float: none; + width: auto; + margin-top: 0; + background-color: transparent; + border: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-nav .open .dropdown-menu > li > a, + .navbar-nav .open .dropdown-menu .dropdown-header { + padding: 5px 15px 5px 25px; + } + .navbar-nav .open .dropdown-menu > li > a { + line-height: 20px; + } + .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-nav .open .dropdown-menu > li > a:focus { + background-image: none; + } +} +@media (min-width: 768px) { + .navbar-nav { + float: left; + margin: 0; + } + .navbar-nav > li { + float: left; + } + .navbar-nav > li > a { + padding-top: 15px; + padding-bottom: 15px; + } + .navbar-nav.navbar-right:last-child { + margin-right: -15px; + } +} +@media (min-width: 768px) { + .navbar-left { + float: left !important; + } + .navbar-right { + float: right !important; + } +} +.navbar-form { + padding: 10px 15px; + margin-top: 8px; + margin-right: -15px; + margin-bottom: 8px; + margin-left: -15px; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); +} +@media (min-width: 768px) { + .navbar-form .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .navbar-form .input-group { + display: inline-table; + vertical-align: middle; + } + .navbar-form .input-group .input-group-addon, + .navbar-form .input-group .input-group-btn, + .navbar-form .input-group .form-control { + width: auto; + } + .navbar-form .input-group > .form-control { + width: 100%; + } + .navbar-form .control-label { + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .radio, + .navbar-form .checkbox { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .radio label, + .navbar-form .checkbox label { + padding-left: 0; + } + .navbar-form .radio input[type="radio"], + .navbar-form .checkbox input[type="checkbox"] { + position: relative; + margin-left: 0; + } + .navbar-form .has-feedback .form-control-feedback { + top: 0; + } +} +@media (max-width: 767px) { + .navbar-form .form-group { + margin-bottom: 5px; + } +} +@media (min-width: 768px) { + .navbar-form { + width: auto; + padding-top: 0; + padding-bottom: 0; + margin-right: 0; + margin-left: 0; + border: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-form.navbar-right:last-child { + margin-right: -15px; + } +} +.navbar-nav > li > .dropdown-menu { + margin-top: 0; + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.navbar-btn { + margin-top: 8px; + margin-bottom: 8px; +} +.navbar-btn.btn-sm { + margin-top: 10px; + margin-bottom: 10px; +} +.navbar-btn.btn-xs { + margin-top: 14px; + margin-bottom: 14px; +} +.navbar-text { + margin-top: 15px; + margin-bottom: 15px; +} +@media (min-width: 768px) { + .navbar-text { + float: left; + margin-right: 15px; + margin-left: 15px; + } + .navbar-text.navbar-right:last-child { + margin-right: 0; + } +} +.navbar-default { + background-color: #f8f8f8; + border-color: #e7e7e7; +} +.navbar-default .navbar-brand { + color: #777; +} +.navbar-default .navbar-brand:hover, +.navbar-default .navbar-brand:focus { + color: #5e5e5e; + background-color: transparent; +} +.navbar-default .navbar-text { + color: #777; +} +.navbar-default .navbar-nav > li > a { + color: #777; +} +.navbar-default .navbar-nav > li > a:hover, +.navbar-default .navbar-nav > li > a:focus { + color: #333; + background-color: transparent; +} +.navbar-default .navbar-nav > .active > a, +.navbar-default .navbar-nav > .active > a:hover, +.navbar-default .navbar-nav > .active > a:focus { + color: #555; + background-color: #e7e7e7; +} +.navbar-default .navbar-nav > .disabled > a, +.navbar-default .navbar-nav > .disabled > a:hover, +.navbar-default .navbar-nav > .disabled > a:focus { + color: #ccc; + background-color: transparent; +} +.navbar-default .navbar-toggle { + border-color: #ddd; +} +.navbar-default .navbar-toggle:hover, +.navbar-default .navbar-toggle:focus { + background-color: #ddd; +} +.navbar-default .navbar-toggle .icon-bar { + background-color: #888; +} +.navbar-default .navbar-collapse, +.navbar-default .navbar-form { + border-color: #e7e7e7; +} +.navbar-default .navbar-nav > .open > a, +.navbar-default .navbar-nav > .open > a:hover, +.navbar-default .navbar-nav > .open > a:focus { + color: #555; + background-color: #e7e7e7; +} +@media (max-width: 767px) { + .navbar-default .navbar-nav .open .dropdown-menu > li > a { + color: #777; + } + .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { + color: #333; + background-color: transparent; + } + .navbar-default .navbar-nav .open .dropdown-menu > .active > a, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #555; + background-color: #e7e7e7; + } + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #ccc; + background-color: transparent; + } +} +.navbar-default .navbar-link { + color: #777; +} +.navbar-default .navbar-link:hover { + color: #333; +} +.navbar-default .btn-link { + color: #777; +} +.navbar-default .btn-link:hover, +.navbar-default .btn-link:focus { + color: #333; +} +.navbar-default .btn-link[disabled]:hover, +fieldset[disabled] .navbar-default .btn-link:hover, +.navbar-default .btn-link[disabled]:focus, +fieldset[disabled] .navbar-default .btn-link:focus { + color: #ccc; +} +.navbar-inverse { + background-color: #222; + border-color: #080808; +} +.navbar-inverse .navbar-brand { + color: #777; +} +.navbar-inverse .navbar-brand:hover, +.navbar-inverse .navbar-brand:focus { + color: #fff; + background-color: transparent; +} +.navbar-inverse .navbar-text { + color: #777; +} +.navbar-inverse .navbar-nav > li > a { + color: #777; +} +.navbar-inverse .navbar-nav > li > a:hover, +.navbar-inverse .navbar-nav > li > a:focus { + color: #fff; + background-color: transparent; +} +.navbar-inverse .navbar-nav > .active > a, +.navbar-inverse .navbar-nav > .active > a:hover, +.navbar-inverse .navbar-nav > .active > a:focus { + color: #fff; + background-color: #080808; +} +.navbar-inverse .navbar-nav > .disabled > a, +.navbar-inverse .navbar-nav > .disabled > a:hover, +.navbar-inverse .navbar-nav > .disabled > a:focus { + color: #444; + background-color: transparent; +} +.navbar-inverse .navbar-toggle { + border-color: #333; +} +.navbar-inverse .navbar-toggle:hover, +.navbar-inverse .navbar-toggle:focus { + background-color: #333; +} +.navbar-inverse .navbar-toggle .icon-bar { + background-color: #fff; +} +.navbar-inverse .navbar-collapse, +.navbar-inverse .navbar-form { + border-color: #101010; +} +.navbar-inverse .navbar-nav > .open > a, +.navbar-inverse .navbar-nav > .open > a:hover, +.navbar-inverse .navbar-nav > .open > a:focus { + color: #fff; + background-color: #080808; +} +@media (max-width: 767px) { + .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { + border-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu .divider { + background-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { + color: #777; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { + color: #fff; + background-color: transparent; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #fff; + background-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #444; + background-color: transparent; + } +} +.navbar-inverse .navbar-link { + color: #777; +} +.navbar-inverse .navbar-link:hover { + color: #fff; +} +.navbar-inverse .btn-link { + color: #777; +} +.navbar-inverse .btn-link:hover, +.navbar-inverse .btn-link:focus { + color: #fff; +} +.navbar-inverse .btn-link[disabled]:hover, +fieldset[disabled] .navbar-inverse .btn-link:hover, +.navbar-inverse .btn-link[disabled]:focus, +fieldset[disabled] .navbar-inverse .btn-link:focus { + color: #444; +} +.breadcrumb { + padding: 8px 15px; + margin-bottom: 20px; + list-style: none; + background-color: #f5f5f5; + border-radius: 4px; +} +.breadcrumb > li { + display: inline-block; +} +.breadcrumb > li + li:before { + padding: 0 5px; + color: #ccc; + content: "/\00a0"; +} +.breadcrumb > .active { + color: #777; +} +.pagination { + display: inline-block; + padding-left: 0; + margin: 20px 0; + border-radius: 4px; +} +.pagination > li { + display: inline; +} +.pagination > li > a, +.pagination > li > span { + position: relative; + float: left; + padding: 6px 12px; + margin-left: -1px; + line-height: 1.42857143; + color: #428bca; + text-decoration: none; + background-color: #fff; + border: 1px solid #ddd; +} +.pagination > li:first-child > a, +.pagination > li:first-child > span { + margin-left: 0; + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; +} +.pagination > li:last-child > a, +.pagination > li:last-child > span { + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; +} +.pagination > li > a:hover, +.pagination > li > span:hover, +.pagination > li > a:focus, +.pagination > li > span:focus { + color: #2a6496; + background-color: #eee; + border-color: #ddd; +} +.pagination > .active > a, +.pagination > .active > span, +.pagination > .active > a:hover, +.pagination > .active > span:hover, +.pagination > .active > a:focus, +.pagination > .active > span:focus { + z-index: 2; + color: #fff; + cursor: default; + background-color: #428bca; + border-color: #428bca; +} +.pagination > .disabled > span, +.pagination > .disabled > span:hover, +.pagination > .disabled > span:focus, +.pagination > .disabled > a, +.pagination > .disabled > a:hover, +.pagination > .disabled > a:focus { + color: #777; + cursor: not-allowed; + background-color: #fff; + border-color: #ddd; +} +.pagination-lg > li > a, +.pagination-lg > li > span { + padding: 10px 16px; + font-size: 18px; +} +.pagination-lg > li:first-child > a, +.pagination-lg > li:first-child > span { + border-top-left-radius: 6px; + border-bottom-left-radius: 6px; +} +.pagination-lg > li:last-child > a, +.pagination-lg > li:last-child > span { + border-top-right-radius: 6px; + border-bottom-right-radius: 6px; +} +.pagination-sm > li > a, +.pagination-sm > li > span { + padding: 5px 10px; + font-size: 12px; +} +.pagination-sm > li:first-child > a, +.pagination-sm > li:first-child > span { + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; +} +.pagination-sm > li:last-child > a, +.pagination-sm > li:last-child > span { + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; +} +.pager { + padding-left: 0; + margin: 20px 0; + text-align: center; + list-style: none; +} +.pager li { + display: inline; +} +.pager li > a, +.pager li > span { + display: inline-block; + padding: 5px 14px; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 15px; +} +.pager li > a:hover, +.pager li > a:focus { + text-decoration: none; + background-color: #eee; +} +.pager .next > a, +.pager .next > span { + float: right; +} +.pager .previous > a, +.pager .previous > span { + float: left; +} +.pager .disabled > a, +.pager .disabled > a:hover, +.pager .disabled > a:focus, +.pager .disabled > span { + color: #777; + cursor: not-allowed; + background-color: #fff; +} +.label { + display: inline; + padding: .2em .6em .3em; + font-size: 75%; + font-weight: bold; + line-height: 1; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: .25em; +} +a.label:hover, +a.label:focus { + color: #fff; + text-decoration: none; + cursor: pointer; +} +.label:empty { + display: none; +} +.btn .label { + position: relative; + top: -1px; +} +.label-default { + background-color: #777; +} +.label-default[href]:hover, +.label-default[href]:focus { + background-color: #5e5e5e; +} +.label-primary { + background-color: #428bca; +} +.label-primary[href]:hover, +.label-primary[href]:focus { + background-color: #3071a9; +} +.label-success { + background-color: #5cb85c; +} +.label-success[href]:hover, +.label-success[href]:focus { + background-color: #449d44; +} +.label-info { + background-color: #5bc0de; +} +.label-info[href]:hover, +.label-info[href]:focus { + background-color: #31b0d5; +} +.label-warning { + background-color: #f0ad4e; +} +.label-warning[href]:hover, +.label-warning[href]:focus { + background-color: #ec971f; +} +.label-danger { + background-color: #d9534f; +} +.label-danger[href]:hover, +.label-danger[href]:focus { + background-color: #c9302c; +} +.badge { + display: inline-block; + min-width: 10px; + padding: 3px 7px; + font-size: 12px; + font-weight: bold; + line-height: 1; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + background-color: #777; + border-radius: 10px; +} +.badge:empty { + display: none; +} +.btn .badge { + position: relative; + top: -1px; +} +.btn-xs .badge { + top: 0; + padding: 1px 5px; +} +a.badge:hover, +a.badge:focus { + color: #fff; + text-decoration: none; + cursor: pointer; +} +a.list-group-item.active > .badge, +.nav-pills > .active > a > .badge { + color: #428bca; + background-color: #fff; +} +.nav-pills > li > a > .badge { + margin-left: 3px; +} +.jumbotron { + padding: 30px; + margin-bottom: 30px; + color: inherit; + background-color: #eee; +} +.jumbotron h1, +.jumbotron .h1 { + color: inherit; +} +.jumbotron p { + margin-bottom: 15px; + font-size: 21px; + font-weight: 200; +} +.jumbotron > hr { + border-top-color: #d5d5d5; +} +.container .jumbotron { + border-radius: 6px; +} +.jumbotron .container { + max-width: 100%; +} +@media screen and (min-width: 768px) { + .jumbotron { + padding-top: 48px; + padding-bottom: 48px; + } + .container .jumbotron { + padding-right: 60px; + padding-left: 60px; + } + .jumbotron h1, + .jumbotron .h1 { + font-size: 63px; + } +} +.thumbnail { + display: block; + padding: 4px; + margin-bottom: 20px; + line-height: 1.42857143; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 4px; + -webkit-transition: all .2s ease-in-out; + -o-transition: all .2s ease-in-out; + transition: all .2s ease-in-out; +} +.thumbnail > img, +.thumbnail a > img { + margin-right: auto; + margin-left: auto; +} +a.thumbnail:hover, +a.thumbnail:focus, +a.thumbnail.active { + border-color: #428bca; +} +.thumbnail .caption { + padding: 9px; + color: #333; +} +.alert { + padding: 15px; + margin-bottom: 20px; + border: 1px solid transparent; + border-radius: 4px; +} +.alert h4 { + margin-top: 0; + color: inherit; +} +.alert .alert-link { + font-weight: bold; +} +.alert > p, +.alert > ul { + margin-bottom: 0; +} +.alert > p + p { + margin-top: 5px; +} +.alert-dismissable, +.alert-dismissible { + padding-right: 35px; +} +.alert-dismissable .close, +.alert-dismissible .close { + position: relative; + top: -2px; + right: -21px; + color: inherit; +} +.alert-success { + color: #3c763d; + background-color: #dff0d8; + border-color: #d6e9c6; +} +.alert-success hr { + border-top-color: #c9e2b3; +} +.alert-success .alert-link { + color: #2b542c; +} +.alert-info { + color: #31708f; + background-color: #d9edf7; + border-color: #bce8f1; +} +.alert-info hr { + border-top-color: #a6e1ec; +} +.alert-info .alert-link { + color: #245269; +} +.alert-warning { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc; +} +.alert-warning hr { + border-top-color: #f7e1b5; +} +.alert-warning .alert-link { + color: #66512c; +} +.alert-danger { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1; +} +.alert-danger hr { + border-top-color: #e4b9c0; +} +.alert-danger .alert-link { + color: #843534; +} +@-webkit-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@-o-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +.progress { + height: 20px; + margin-bottom: 20px; + overflow: hidden; + background-color: #f5f5f5; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); +} +.progress-bar { + float: left; + width: 0; + height: 100%; + font-size: 12px; + line-height: 20px; + color: #fff; + text-align: center; + background-color: #428bca; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + -webkit-transition: width .6s ease; + -o-transition: width .6s ease; + transition: width .6s ease; +} +.progress-striped .progress-bar, +.progress-bar-striped { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + -webkit-background-size: 40px 40px; + background-size: 40px 40px; +} +.progress.active .progress-bar, +.progress-bar.active { + -webkit-animation: progress-bar-stripes 2s linear infinite; + -o-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite; +} +.progress-bar[aria-valuenow="1"], +.progress-bar[aria-valuenow="2"] { + min-width: 30px; +} +.progress-bar[aria-valuenow="0"] { + min-width: 30px; + color: #777; + background-color: transparent; + background-image: none; + -webkit-box-shadow: none; + box-shadow: none; +} +.progress-bar-success { + background-color: #5cb85c; +} +.progress-striped .progress-bar-success { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.progress-bar-info { + background-color: #5bc0de; +} +.progress-striped .progress-bar-info { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.progress-bar-warning { + background-color: #f0ad4e; +} +.progress-striped .progress-bar-warning { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.progress-bar-danger { + background-color: #d9534f; +} +.progress-striped .progress-bar-danger { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.media, +.media-body { + overflow: hidden; + zoom: 1; +} +.media, +.media .media { + margin-top: 15px; +} +.media:first-child { + margin-top: 0; +} +.media-object { + display: block; +} +.media-heading { + margin: 0 0 5px; +} +.media > .pull-left { + margin-right: 10px; +} +.media > .pull-right { + margin-left: 10px; +} +.media-list { + padding-left: 0; + list-style: none; +} +.list-group { + padding-left: 0; + margin-bottom: 20px; +} +.list-group-item { + position: relative; + display: block; + padding: 10px 15px; + margin-bottom: -1px; + background-color: #fff; + border: 1px solid #ddd; +} +.list-group-item:first-child { + border-top-left-radius: 4px; + border-top-right-radius: 4px; +} +.list-group-item:last-child { + margin-bottom: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +.list-group-item > .badge { + float: right; +} +.list-group-item > .badge + .badge { + margin-right: 5px; +} +a.list-group-item { + color: #555; +} +a.list-group-item .list-group-item-heading { + color: #333; +} +a.list-group-item:hover, +a.list-group-item:focus { + color: #555; + text-decoration: none; + background-color: #f5f5f5; +} +.list-group-item.disabled, +.list-group-item.disabled:hover, +.list-group-item.disabled:focus { + color: #777; + background-color: #eee; +} +.list-group-item.disabled .list-group-item-heading, +.list-group-item.disabled:hover .list-group-item-heading, +.list-group-item.disabled:focus .list-group-item-heading { + color: inherit; +} +.list-group-item.disabled .list-group-item-text, +.list-group-item.disabled:hover .list-group-item-text, +.list-group-item.disabled:focus .list-group-item-text { + color: #777; +} +.list-group-item.active, +.list-group-item.active:hover, +.list-group-item.active:focus { + z-index: 2; + color: #fff; + background-color: #428bca; + border-color: #428bca; +} +.list-group-item.active .list-group-item-heading, +.list-group-item.active:hover .list-group-item-heading, +.list-group-item.active:focus .list-group-item-heading, +.list-group-item.active .list-group-item-heading > small, +.list-group-item.active:hover .list-group-item-heading > small, +.list-group-item.active:focus .list-group-item-heading > small, +.list-group-item.active .list-group-item-heading > .small, +.list-group-item.active:hover .list-group-item-heading > .small, +.list-group-item.active:focus .list-group-item-heading > .small { + color: inherit; +} +.list-group-item.active .list-group-item-text, +.list-group-item.active:hover .list-group-item-text, +.list-group-item.active:focus .list-group-item-text { + color: #e1edf7; +} +.list-group-item-success { + color: #3c763d; + background-color: #dff0d8; +} +a.list-group-item-success { + color: #3c763d; +} +a.list-group-item-success .list-group-item-heading { + color: inherit; +} +a.list-group-item-success:hover, +a.list-group-item-success:focus { + color: #3c763d; + background-color: #d0e9c6; +} +a.list-group-item-success.active, +a.list-group-item-success.active:hover, +a.list-group-item-success.active:focus { + color: #fff; + background-color: #3c763d; + border-color: #3c763d; +} +.list-group-item-info { + color: #31708f; + background-color: #d9edf7; +} +a.list-group-item-info { + color: #31708f; +} +a.list-group-item-info .list-group-item-heading { + color: inherit; +} +a.list-group-item-info:hover, +a.list-group-item-info:focus { + color: #31708f; + background-color: #c4e3f3; +} +a.list-group-item-info.active, +a.list-group-item-info.active:hover, +a.list-group-item-info.active:focus { + color: #fff; + background-color: #31708f; + border-color: #31708f; +} +.list-group-item-warning { + color: #8a6d3b; + background-color: #fcf8e3; +} +a.list-group-item-warning { + color: #8a6d3b; +} +a.list-group-item-warning .list-group-item-heading { + color: inherit; +} +a.list-group-item-warning:hover, +a.list-group-item-warning:focus { + color: #8a6d3b; + background-color: #faf2cc; +} +a.list-group-item-warning.active, +a.list-group-item-warning.active:hover, +a.list-group-item-warning.active:focus { + color: #fff; + background-color: #8a6d3b; + border-color: #8a6d3b; +} +.list-group-item-danger { + color: #a94442; + background-color: #f2dede; +} +a.list-group-item-danger { + color: #a94442; +} +a.list-group-item-danger .list-group-item-heading { + color: inherit; +} +a.list-group-item-danger:hover, +a.list-group-item-danger:focus { + color: #a94442; + background-color: #ebcccc; +} +a.list-group-item-danger.active, +a.list-group-item-danger.active:hover, +a.list-group-item-danger.active:focus { + color: #fff; + background-color: #a94442; + border-color: #a94442; +} +.list-group-item-heading { + margin-top: 0; + margin-bottom: 5px; +} +.list-group-item-text { + margin-bottom: 0; + line-height: 1.3; +} +.panel { + margin-bottom: 20px; + background-color: #fff; + border: 1px solid transparent; + border-radius: 4px; + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05); + box-shadow: 0 1px 1px rgba(0, 0, 0, .05); +} +.panel-body { + padding: 15px; +} +.panel-heading { + padding: 10px 15px; + border-bottom: 1px solid transparent; + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel-heading > .dropdown .dropdown-toggle { + color: inherit; +} +.panel-title { + margin-top: 0; + margin-bottom: 0; + font-size: 16px; + color: inherit; +} +.panel-title > a { + color: inherit; +} +.panel-footer { + padding: 10px 15px; + background-color: #f5f5f5; + border-top: 1px solid #ddd; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .list-group { + margin-bottom: 0; +} +.panel > .list-group .list-group-item { + border-width: 1px 0; + border-radius: 0; +} +.panel > .list-group:first-child .list-group-item:first-child { + border-top: 0; + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .list-group:last-child .list-group-item:last-child { + border-bottom: 0; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel-heading + .list-group .list-group-item:first-child { + border-top-width: 0; +} +.list-group + .panel-footer { + border-top-width: 0; +} +.panel > .table, +.panel > .table-responsive > .table, +.panel > .panel-collapse > .table { + margin-bottom: 0; +} +.panel > .table:first-child, +.panel > .table-responsive:first-child > .table:first-child { + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child { + border-top-left-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child { + border-top-right-radius: 3px; +} +.panel > .table:last-child, +.panel > .table-responsive:last-child > .table:last-child { + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child { + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child { + border-bottom-right-radius: 3px; +} +.panel > .panel-body + .table, +.panel > .panel-body + .table-responsive { + border-top: 1px solid #ddd; +} +.panel > .table > tbody:first-child > tr:first-child th, +.panel > .table > tbody:first-child > tr:first-child td { + border-top: 0; +} +.panel > .table-bordered, +.panel > .table-responsive > .table-bordered { + border: 0; +} +.panel > .table-bordered > thead > tr > th:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:first-child, +.panel > .table-bordered > tbody > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, +.panel > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-bordered > thead > tr > td:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:first-child, +.panel > .table-bordered > tbody > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, +.panel > .table-bordered > tfoot > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; +} +.panel > .table-bordered > thead > tr > th:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:last-child, +.panel > .table-bordered > tbody > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, +.panel > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-bordered > thead > tr > td:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:last-child, +.panel > .table-bordered > tbody > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, +.panel > .table-bordered > tfoot > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; +} +.panel > .table-bordered > thead > tr:first-child > td, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > td, +.panel > .table-bordered > tbody > tr:first-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td, +.panel > .table-bordered > thead > tr:first-child > th, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > th, +.panel > .table-bordered > tbody > tr:first-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th { + border-bottom: 0; +} +.panel > .table-bordered > tbody > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, +.panel > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-bordered > tbody > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, +.panel > .table-bordered > tfoot > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th { + border-bottom: 0; +} +.panel > .table-responsive { + margin-bottom: 0; + border: 0; +} +.panel-group { + margin-bottom: 20px; +} +.panel-group .panel { + margin-bottom: 0; + border-radius: 4px; +} +.panel-group .panel + .panel { + margin-top: 5px; +} +.panel-group .panel-heading { + border-bottom: 0; +} +.panel-group .panel-heading + .panel-collapse > .panel-body { + border-top: 1px solid #ddd; +} +.panel-group .panel-footer { + border-top: 0; +} +.panel-group .panel-footer + .panel-collapse .panel-body { + border-bottom: 1px solid #ddd; +} +.panel-default { + border-color: #ddd; +} +.panel-default > .panel-heading { + color: #333; + background-color: #f5f5f5; + border-color: #ddd; +} +.panel-default > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #ddd; +} +.panel-default > .panel-heading .badge { + color: #f5f5f5; + background-color: #333; +} +.panel-default > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #ddd; +} +.panel-primary { + border-color: #428bca; +} +.panel-primary > .panel-heading { + color: #fff; + background-color: #428bca; + border-color: #428bca; +} +.panel-primary > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #428bca; +} +.panel-primary > .panel-heading .badge { + color: #428bca; + background-color: #fff; +} +.panel-primary > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #428bca; +} +.panel-success { + border-color: #d6e9c6; +} +.panel-success > .panel-heading { + color: #3c763d; + background-color: #dff0d8; + border-color: #d6e9c6; +} +.panel-success > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #d6e9c6; +} +.panel-success > .panel-heading .badge { + color: #dff0d8; + background-color: #3c763d; +} +.panel-success > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #d6e9c6; +} +.panel-info { + border-color: #bce8f1; +} +.panel-info > .panel-heading { + color: #31708f; + background-color: #d9edf7; + border-color: #bce8f1; +} +.panel-info > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #bce8f1; +} +.panel-info > .panel-heading .badge { + color: #d9edf7; + background-color: #31708f; +} +.panel-info > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #bce8f1; +} +.panel-warning { + border-color: #faebcc; +} +.panel-warning > .panel-heading { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc; +} +.panel-warning > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #faebcc; +} +.panel-warning > .panel-heading .badge { + color: #fcf8e3; + background-color: #8a6d3b; +} +.panel-warning > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #faebcc; +} +.panel-danger { + border-color: #ebccd1; +} +.panel-danger > .panel-heading { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1; +} +.panel-danger > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #ebccd1; +} +.panel-danger > .panel-heading .badge { + color: #f2dede; + background-color: #a94442; +} +.panel-danger > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #ebccd1; +} +.embed-responsive { + position: relative; + display: block; + height: 0; + padding: 0; + overflow: hidden; +} +.embed-responsive .embed-responsive-item, +.embed-responsive iframe, +.embed-responsive embed, +.embed-responsive object { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 100%; + height: 100%; + border: 0; +} +.embed-responsive.embed-responsive-16by9 { + padding-bottom: 56.25%; +} +.embed-responsive.embed-responsive-4by3 { + padding-bottom: 75%; +} +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #f5f5f5; + border: 1px solid #e3e3e3; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); +} +.well blockquote { + border-color: #ddd; + border-color: rgba(0, 0, 0, .15); +} +.well-lg { + padding: 24px; + border-radius: 6px; +} +.well-sm { + padding: 9px; + border-radius: 3px; +} +.close { + float: right; + font-size: 21px; + font-weight: bold; + line-height: 1; + color: #000; + text-shadow: 0 1px 0 #fff; + filter: alpha(opacity=20); + opacity: .2; +} +.close:hover, +.close:focus { + color: #000; + text-decoration: none; + cursor: pointer; + filter: alpha(opacity=50); + opacity: .5; +} +button.close { + -webkit-appearance: none; + padding: 0; + cursor: pointer; + background: transparent; + border: 0; +} +.modal-open { + overflow: hidden; +} +.modal { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1050; + display: none; + overflow: hidden; + -webkit-overflow-scrolling: touch; + outline: 0; +} +.modal.fade .modal-dialog { + -webkit-transition: -webkit-transform .3s ease-out; + -o-transition: -o-transform .3s ease-out; + transition: transform .3s ease-out; + -webkit-transform: translate3d(0, -25%, 0); + -o-transform: translate3d(0, -25%, 0); + transform: translate3d(0, -25%, 0); +} +.modal.in .modal-dialog { + -webkit-transform: translate3d(0, 0, 0); + -o-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); +} +.modal-open .modal { + overflow-x: hidden; + overflow-y: auto; +} +.modal-dialog { + position: relative; + width: auto; + margin: 10px; +} +.modal-content { + position: relative; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #999; + border: 1px solid rgba(0, 0, 0, .2); + border-radius: 6px; + outline: 0; + -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5); + box-shadow: 0 3px 9px rgba(0, 0, 0, .5); +} +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + background-color: #000; +} +.modal-backdrop.fade { + filter: alpha(opacity=0); + opacity: 0; +} +.modal-backdrop.in { + filter: alpha(opacity=50); + opacity: .5; +} +.modal-header { + min-height: 16.42857143px; + padding: 15px; + border-bottom: 1px solid #e5e5e5; +} +.modal-header .close { + margin-top: -2px; +} +.modal-title { + margin: 0; + line-height: 1.42857143; +} +.modal-body { + position: relative; + padding: 15px; +} +.modal-footer { + padding: 15px; + text-align: right; + border-top: 1px solid #e5e5e5; +} +.modal-footer .btn + .btn { + margin-bottom: 0; + margin-left: 5px; +} +.modal-footer .btn-group .btn + .btn { + margin-left: -1px; +} +.modal-footer .btn-block + .btn-block { + margin-left: 0; +} +.modal-scrollbar-measure { + position: absolute; + top: -9999px; + width: 50px; + height: 50px; + overflow: scroll; +} +@media (min-width: 768px) { + .modal-dialog { + width: 600px; + margin: 30px auto; + } + .modal-content { + -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5); + box-shadow: 0 5px 15px rgba(0, 0, 0, .5); + } + .modal-sm { + width: 300px; + } +} +@media (min-width: 992px) { + .modal-lg { + width: 900px; + } +} +.tooltip { + position: absolute; + z-index: 1070; + display: block; + font-size: 12px; + line-height: 1.4; + visibility: visible; + filter: alpha(opacity=0); + opacity: 0; +} +.tooltip.in { + filter: alpha(opacity=90); + opacity: .9; +} +.tooltip.top { + padding: 5px 0; + margin-top: -3px; +} +.tooltip.right { + padding: 0 5px; + margin-left: 3px; +} +.tooltip.bottom { + padding: 5px 0; + margin-top: 3px; +} +.tooltip.left { + padding: 0 5px; + margin-left: -3px; +} +.tooltip-inner { + max-width: 200px; + padding: 3px 8px; + color: #fff; + text-align: center; + text-decoration: none; + background-color: #000; + border-radius: 4px; +} +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.top-left .tooltip-arrow { + bottom: 0; + left: 5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.top-right .tooltip-arrow { + right: 5px; + bottom: 0; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-width: 5px 5px 5px 0; + border-right-color: #000; +} +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-width: 5px 0 5px 5px; + border-left-color: #000; +} +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.tooltip.bottom-left .tooltip-arrow { + top: 0; + left: 5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.tooltip.bottom-right .tooltip-arrow { + top: 0; + right: 5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1060; + display: none; + max-width: 276px; + padding: 1px; + text-align: left; + white-space: normal; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, .2); + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2); + box-shadow: 0 5px 10px rgba(0, 0, 0, .2); +} +.popover.top { + margin-top: -10px; +} +.popover.right { + margin-left: 10px; +} +.popover.bottom { + margin-top: 10px; +} +.popover.left { + margin-left: -10px; +} +.popover-title { + padding: 8px 14px; + margin: 0; + font-size: 14px; + font-weight: normal; + line-height: 18px; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + border-radius: 5px 5px 0 0; +} +.popover-content { + padding: 9px 14px; +} +.popover > .arrow, +.popover > .arrow:after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.popover > .arrow { + border-width: 11px; +} +.popover > .arrow:after { + content: ""; + border-width: 10px; +} +.popover.top > .arrow { + bottom: -11px; + left: 50%; + margin-left: -11px; + border-top-color: #999; + border-top-color: rgba(0, 0, 0, .25); + border-bottom-width: 0; +} +.popover.top > .arrow:after { + bottom: 1px; + margin-left: -10px; + content: " "; + border-top-color: #fff; + border-bottom-width: 0; +} +.popover.right > .arrow { + top: 50%; + left: -11px; + margin-top: -11px; + border-right-color: #999; + border-right-color: rgba(0, 0, 0, .25); + border-left-width: 0; +} +.popover.right > .arrow:after { + bottom: -10px; + left: 1px; + content: " "; + border-right-color: #fff; + border-left-width: 0; +} +.popover.bottom > .arrow { + top: -11px; + left: 50%; + margin-left: -11px; + border-top-width: 0; + border-bottom-color: #999; + border-bottom-color: rgba(0, 0, 0, .25); +} +.popover.bottom > .arrow:after { + top: 1px; + margin-left: -10px; + content: " "; + border-top-width: 0; + border-bottom-color: #fff; +} +.popover.left > .arrow { + top: 50%; + right: -11px; + margin-top: -11px; + border-right-width: 0; + border-left-color: #999; + border-left-color: rgba(0, 0, 0, .25); +} +.popover.left > .arrow:after { + right: 1px; + bottom: -10px; + content: " "; + border-right-width: 0; + border-left-color: #fff; +} +.carousel { + position: relative; +} +.carousel-inner { + position: relative; + width: 100%; + overflow: hidden; +} +.carousel-inner > .item { + position: relative; + display: none; + -webkit-transition: .6s ease-in-out left; + -o-transition: .6s ease-in-out left; + transition: .6s ease-in-out left; +} +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + line-height: 1; +} +.carousel-inner > .active, +.carousel-inner > .next, +.carousel-inner > .prev { + display: block; +} +.carousel-inner > .active { + left: 0; +} +.carousel-inner > .next, +.carousel-inner > .prev { + position: absolute; + top: 0; + width: 100%; +} +.carousel-inner > .next { + left: 100%; +} +.carousel-inner > .prev { + left: -100%; +} +.carousel-inner > .next.left, +.carousel-inner > .prev.right { + left: 0; +} +.carousel-inner > .active.left { + left: -100%; +} +.carousel-inner > .active.right { + left: 100%; +} +.carousel-control { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 15%; + font-size: 20px; + color: #fff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, .6); + filter: alpha(opacity=50); + opacity: .5; +} +.carousel-control.left { + background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); + background-image: -o-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); + background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .5)), to(rgba(0, 0, 0, .0001))); + background-image: linear-gradient(to right, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); + background-repeat: repeat-x; +} +.carousel-control.right { + right: 0; + left: auto; + background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); + background-image: -o-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); + background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .0001)), to(rgba(0, 0, 0, .5))); + background-image: linear-gradient(to right, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); + background-repeat: repeat-x; +} +.carousel-control:hover, +.carousel-control:focus { + color: #fff; + text-decoration: none; + filter: alpha(opacity=90); + outline: 0; + opacity: .9; +} +.carousel-control .icon-prev, +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-left, +.carousel-control .glyphicon-chevron-right { + position: absolute; + top: 50%; + z-index: 5; + display: inline-block; +} +.carousel-control .icon-prev, +.carousel-control .glyphicon-chevron-left { + left: 50%; + margin-left: -10px; +} +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-right { + right: 50%; + margin-right: -10px; +} +.carousel-control .icon-prev, +.carousel-control .icon-next { + width: 20px; + height: 20px; + margin-top: -10px; + font-family: serif; +} +.carousel-control .icon-prev:before { + content: '\2039'; +} +.carousel-control .icon-next:before { + content: '\203a'; +} +.carousel-indicators { + position: absolute; + bottom: 10px; + left: 50%; + z-index: 15; + width: 60%; + padding-left: 0; + margin-left: -30%; + text-align: center; + list-style: none; +} +.carousel-indicators li { + display: inline-block; + width: 10px; + height: 10px; + margin: 1px; + text-indent: -999px; + cursor: pointer; + background-color: #000 \9; + background-color: rgba(0, 0, 0, 0); + border: 1px solid #fff; + border-radius: 10px; +} +.carousel-indicators .active { + width: 12px; + height: 12px; + margin: 0; + background-color: #fff; +} +.carousel-caption { + position: absolute; + right: 15%; + bottom: 20px; + left: 15%; + z-index: 10; + padding-top: 20px; + padding-bottom: 20px; + color: #fff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, .6); +} +.carousel-caption .btn { + text-shadow: none; +} +@media screen and (min-width: 768px) { + .carousel-control .glyphicon-chevron-left, + .carousel-control .glyphicon-chevron-right, + .carousel-control .icon-prev, + .carousel-control .icon-next { + width: 30px; + height: 30px; + margin-top: -15px; + font-size: 30px; + } + .carousel-control .glyphicon-chevron-left, + .carousel-control .icon-prev { + margin-left: -15px; + } + .carousel-control .glyphicon-chevron-right, + .carousel-control .icon-next { + margin-right: -15px; + } + .carousel-caption { + right: 20%; + left: 20%; + padding-bottom: 30px; + } + .carousel-indicators { + bottom: 20px; + } +} +.clearfix:before, +.clearfix:after, +.dl-horizontal dd:before, +.dl-horizontal dd:after, +.container:before, +.container:after, +.container-fluid:before, +.container-fluid:after, +.row:before, +.row:after, +.form-horizontal .form-group:before, +.form-horizontal .form-group:after, +.btn-toolbar:before, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:before, +.btn-group-vertical > .btn-group:after, +.nav:before, +.nav:after, +.navbar:before, +.navbar:after, +.navbar-header:before, +.navbar-header:after, +.navbar-collapse:before, +.navbar-collapse:after, +.pager:before, +.pager:after, +.panel-body:before, +.panel-body:after, +.modal-footer:before, +.modal-footer:after { + display: table; + content: " "; +} +.clearfix:after, +.dl-horizontal dd:after, +.container:after, +.container-fluid:after, +.row:after, +.form-horizontal .form-group:after, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:after, +.nav:after, +.navbar:after, +.navbar-header:after, +.navbar-collapse:after, +.pager:after, +.panel-body:after, +.modal-footer:after { + clear: both; +} +.center-block { + display: block; + margin-right: auto; + margin-left: auto; +} +.pull-right { + float: right !important; +} +.pull-left { + float: left !important; +} +.hide { + display: none !important; +} +.show { + display: block !important; +} +.invisible { + visibility: hidden; +} +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} +.hidden { + display: none !important; + visibility: hidden !important; +} +.affix { + position: fixed; + -webkit-transform: translate3d(0, 0, 0); + -o-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); +} +@-ms-viewport { + width: device-width; +} +.visible-xs, +.visible-sm, +.visible-md, +.visible-lg { + display: none !important; +} +.visible-xs-block, +.visible-xs-inline, +.visible-xs-inline-block, +.visible-sm-block, +.visible-sm-inline, +.visible-sm-inline-block, +.visible-md-block, +.visible-md-inline, +.visible-md-inline-block, +.visible-lg-block, +.visible-lg-inline, +.visible-lg-inline-block { + display: none !important; +} +@media (max-width: 767px) { + .visible-xs { + display: block !important; + } + table.visible-xs { + display: table; + } + tr.visible-xs { + display: table-row !important; + } + th.visible-xs, + td.visible-xs { + display: table-cell !important; + } +} +@media (max-width: 767px) { + .visible-xs-block { + display: block !important; + } +} +@media (max-width: 767px) { + .visible-xs-inline { + display: inline !important; + } +} +@media (max-width: 767px) { + .visible-xs-inline-block { + display: inline-block !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm { + display: block !important; + } + table.visible-sm { + display: table; + } + tr.visible-sm { + display: table-row !important; + } + th.visible-sm, + td.visible-sm { + display: table-cell !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-block { + display: block !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-inline { + display: inline !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-inline-block { + display: inline-block !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md { + display: block !important; + } + table.visible-md { + display: table; + } + tr.visible-md { + display: table-row !important; + } + th.visible-md, + td.visible-md { + display: table-cell !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-block { + display: block !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-inline { + display: inline !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-inline-block { + display: inline-block !important; + } +} +@media (min-width: 1200px) { + .visible-lg { + display: block !important; + } + table.visible-lg { + display: table; + } + tr.visible-lg { + display: table-row !important; + } + th.visible-lg, + td.visible-lg { + display: table-cell !important; + } +} +@media (min-width: 1200px) { + .visible-lg-block { + display: block !important; + } +} +@media (min-width: 1200px) { + .visible-lg-inline { + display: inline !important; + } +} +@media (min-width: 1200px) { + .visible-lg-inline-block { + display: inline-block !important; + } +} +@media (max-width: 767px) { + .hidden-xs { + display: none !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .hidden-sm { + display: none !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .hidden-md { + display: none !important; + } +} +@media (min-width: 1200px) { + .hidden-lg { + display: none !important; + } +} +.visible-print { + display: none !important; +} +@media print { + .visible-print { + display: block !important; + } + table.visible-print { + display: table; + } + tr.visible-print { + display: table-row !important; + } + th.visible-print, + td.visible-print { + display: table-cell !important; + } +} +.visible-print-block { + display: none !important; +} +@media print { + .visible-print-block { + display: block !important; + } +} +.visible-print-inline { + display: none !important; +} +@media print { + .visible-print-inline { + display: inline !important; + } +} +.visible-print-inline-block { + display: none !important; +} +@media print { + .visible-print-inline-block { + display: inline-block !important; + } +} +@media print { + .hidden-print { + display: none !important; + } +} +/*# sourceMappingURL=bootstrap.css.map */ diff --git a/system/install/css/bootstrap.css.map b/system/install/css/bootstrap.css.map new file mode 100644 index 0000000..bfb5616 --- /dev/null +++ b/system/install/css/bootstrap.css.map @@ -0,0 +1 @@ +{"version":3,"file":"bootstrap.css","sources":["bootstrap.css","less/normalize.less","less/print.less","less/glyphicons.less","less/scaffolding.less","less/mixins/vendor-prefixes.less","less/mixins/tab-focus.less","less/mixins/image.less","less/type.less","less/mixins/text-emphasis.less","less/mixins/background-variant.less","less/mixins/text-overflow.less","less/code.less","less/grid.less","less/mixins/grid.less","less/mixins/grid-framework.less","less/tables.less","less/mixins/table-row.less","less/forms.less","less/mixins/forms.less","less/buttons.less","less/mixins/buttons.less","less/mixins/opacity.less","less/component-animations.less","less/dropdowns.less","less/mixins/nav-divider.less","less/mixins/reset-filter.less","less/button-groups.less","less/mixins/border-radius.less","less/input-groups.less","less/navs.less","less/navbar.less","less/mixins/nav-vertical-align.less","less/utilities.less","less/breadcrumbs.less","less/pagination.less","less/mixins/pagination.less","less/pager.less","less/labels.less","less/mixins/labels.less","less/badges.less","less/jumbotron.less","less/thumbnails.less","less/alerts.less","less/mixins/alerts.less","less/progress-bars.less","less/mixins/gradients.less","less/mixins/progress-bar.less","less/media.less","less/list-group.less","less/mixins/list-group.less","less/panels.less","less/mixins/panels.less","less/responsive-embed.less","less/wells.less","less/close.less","less/modals.less","less/tooltip.less","less/popovers.less","less/carousel.less","less/mixins/clearfix.less","less/mixins/center-block.less","less/mixins/hide-text.less","less/responsive-utilities.less","less/mixins/responsive-visibility.less"],"names":[],"mappings":"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;EAAW,kBAAA;EHoR9C;AGnRmC;EAAW,kBAAA;EHsR9C;AGrRmC;EAAW,kBAAA;EHwR9C;AGvRmC;EAAW,kBAAA;EH0R9C;AGzRmC;EAAW,kBAAA;EH4R9C;AG3RmC;EAAW,kBAAA;EH8R9C;AG7RmC;EAAW,kBAAA;EHgS9C;AG/RmC;EAAW,kBAAA;EHkS9C;AGjSmC;EAAW,kBAAA;EHoS9C;AGnSmC;EAAW,kBAAA;EHsS9C;AGrSmC;EAAW,kBAAA;EHwS9C;AGvSmC;EAAW,kBAAA;EH0S9C;AGzSmC;EAAW,kBAAA;EH4S9C;AG3SmC;EAAW,kBAAA;EH8S9C;AG7SmC;EAAW,kBAAA;EHgT9C;AG/SmC;EAAW,kBAAA;EHkT9C;AGjTmC;EAAW,kBAAA;EHoT9C;AGnTmC;EAAW,kBAAA;EHsT9C;AGrTmC;EAAW,kBAAA;EHwT9C;AGvTmC;EAAW,kBAAA;EH0T9C;AGzTmC;EAAW,kBAAA;EH4T9C;AG3TmC;EAAW,kBAAA;EH8T9C;AG7TmC;EAAW,kBAAA;EHgU9C;AG/TmC;EAAW,kBAAA;EHkU9C;AGjUmC;EAAW,kBAAA;EHoU9C;AGnUmC;EAAW,kBAAA;EHsU9C;AGrUmC;EAAW,kBAAA;EHwU9C;AGvUmC;EAAW,kBAAA;EH0U9C;AGzUmC;EAAW,kBAAA;EH4U9C;AG3UmC;EAAW,kBAAA;EH8U9C;AG7UmC;EAAW,kBAAA;EHgV9C;AG/UmC;EAAW,kBAAA;EHkV9C;AGjVmC;EAAW,kBAAA;EHoV9C;AGnVmC;EAAW,kBAAA;EHsV9C;AGrVmC;EAAW,kBAAA;EHwV9C;AGvVmC;EAAW,kBAAA;EH0V9C;AGzVmC;EAAW,kBAAA;EH4V9C;AG3VmC;EAAW,kBAAA;EH8V9C;AG7VmC;EAAW,kBAAA;EHgW9C;AG/VmC;EAAW,kBAAA;EHkW9C;AGjWmC;EAAW,kBAAA;EHoW9C;AGnWmC;EAAW,kBAAA;EHsW9C;AGrWmC;EAAW,kBAAA;EHwW9C;AGvWmC;EAAW,kBAAA;EH0W9C;AGzWmC;EAAW,kBAAA;EH4W9C;AG3WmC;EAAW,kBAAA;EH8W9C;AG7WmC;EAAW,kBAAA;EHgX9C;AG/WmC;EAAW,kBAAA;EHkX9C;AGjXmC;EAAW,kBAAA;EHoX9C;AGnXmC;EAAW,kBAAA;EHsX9C;AGrXmC;EAAW,kBAAA;EHwX9C;AGvXmC;EAAW,kBAAA;EH0X9C;AGzXmC;EAAW,kBAAA;EH4X9C;AG3XmC;EAAW,kBAAA;EH8X9C;AG7XmC;EAAW,kBAAA;EHgY9C;AG/XmC;EAAW,kBAAA;EHkY9C;AGjYmC;EAAW,kBAAA;EHoY9C;AGnYmC;EAAW,kBAAA;EHsY9C;AGrYmC;EAAW,kBAAA;EHwY9C;AGvYmC;EAAW,kBAAA;EH0Y9C;AGzYmC;EAAW,kBAAA;EH4Y9C;AG3YmC;EAAW,kBAAA;EH8Y9C;AG7YmC;EAAW,kBAAA;EHgZ9C;AG/YmC;EAAW,kBAAA;EHkZ9C;AGjZmC;EAAW,kBAAA;EHoZ9C;AGnZmC;EAAW,kBAAA;EHsZ9C;AGrZmC;EAAW,kBAAA;EHwZ9C;AGvZmC;EAAW,kBAAA;EH0Z9C;AGzZmC;EAAW,kBAAA;EH4Z9C;AG3ZmC;EAAW,kBAAA;EH8Z9C;AG7ZmC;EAAW,kBAAA;EHga9C;AG/ZmC;EAAW,kBAAA;EHka9C;AGjamC;EAAW,kBAAA;EHoa9C;AGnamC;EAAW,kBAAA;EHsa9C;AGramC;EAAW,kBAAA;EHwa9C;AGvamC;EAAW,kBAAA;EH0a9C;AGzamC;EAAW,kBAAA;EH4a9C;AG3amC;EAAW,kBAAA;EH8a9C;AG7amC;EAAW,kBAAA;EHgb9C;AG/amC;EAAW,kBAAA;EHkb9C;AGjbmC;EAAW,kBAAA;EHob9C;AGnbmC;EAAW,kBAAA;EHsb9C;AGrbmC;EAAW,kBAAA;EHwb9C;AGvbmC;EAAW,kBAAA;EH0b9C;AGzbmC;EAAW,kBAAA;EH4b9C;AG3bmC;EAAW,kBAAA;EH8b9C;AG7bmC;EAAW,kBAAA;EHgc9C;AG/bmC;EAAW,kBAAA;EHkc9C;AGjcmC;EAAW,kBAAA;EHoc9C;AGncmC;EAAW,kBAAA;EHsc9C;AGrcmC;EAAW,kBAAA;EHwc9C;AGvcmC;EAAW,kBAAA;EH0c9C;AGzcmC;EAAW,kBAAA;EH4c9C;AG3cmC;EAAW,kBAAA;EH8c9C;AG7cmC;EAAW,kBAAA;EHgd9C;AG/cmC;EAAW,kBAAA;EHkd9C;AGjdmC;EAAW,kBAAA;EHod9C;AGndmC;EAAW,kBAAA;EHsd9C;AGrdmC;EAAW,kBAAA;EHwd9C;AGvdmC;EAAW,kBAAA;EH0d9C;AGzdmC;EAAW,kBAAA;EH4d9C;AG3dmC;EAAW,kBAAA;EH8d9C;AG7dmC;EAAW,kBAAA;EHge9C;AG/dmC;EAAW,kBAAA;EHke9C;AGjemC;EAAW,kBAAA;EHoe9C;AGnemC;EAAW,kBAAA;EHse9C;AGremC;EAAW,kBAAA;EHwe9C;AGvemC;EAAW,kBAAA;EH0e9C;AGzemC;EAAW,kBAAA;EH4e9C;AG3emC;EAAW,kBAAA;EH8e9C;AG7emC;EAAW,kBAAA;EHgf9C;AG/emC;EAAW,kBAAA;EHkf9C;AGjfmC;EAAW,kBAAA;EHof9C;AGnfmC;EAAW,kBAAA;EHsf9C;AGrfmC;EAAW,kBAAA;EHwf9C;AGvfmC;EAAW,kBAAA;EH0f9C;AGzfmC;EAAW,kBAAA;EH4f9C;AG3fmC;EAAW,kBAAA;EH8f9C;AG7fmC;EAAW,kBAAA;EHggB9C;AG/fmC;EAAW,kBAAA;EHkgB9C;AGjgBmC;EAAW,kBAAA;EHogB9C;AGngBmC;EAAW,kBAAA;EHsgB9C;AGrgBmC;EAAW,kBAAA;EHwgB9C;AGvgBmC;EAAW,kBAAA;EH0gB9C;AGzgBmC;EAAW,kBAAA;EH4gB9C;AG3gBmC;EAAW,kBAAA;EH8gB9C;AG7gBmC;EAAW,kBAAA;EHghB9C;AG/gBmC;EAAW,kBAAA;EHkhB9C;AGjhBmC;EAAW,kBAAA;EHohB9C;AGnhBmC;EAAW,kBAAA;EHshB9C;AGrhBmC;EAAW,kBAAA;EHwhB9C;AGvhBmC;EAAW,kBAAA;EH0hB9C;AGzhBmC;EAAW,kBAAA;EH4hB9C;AG3hBmC;EAAW,kBAAA;EH8hB9C;AG7hBmC;EAAW,kBAAA;EHgiB9C;AG/hBmC;EAAW,kBAAA;EHkiB9C;AGjiBmC;EAAW,kBAAA;EHoiB9C;AGniBmC;EAAW,kBAAA;EHsiB9C;AGriBmC;EAAW,kBAAA;EHwiB9C;AGviBmC;EAAW,kBAAA;EH0iB9C;AGziBmC;EAAW,kBAAA;EH4iB9C;AG3iBmC;EAAW,kBAAA;EH8iB9C;AG7iBmC;EA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A;Efk8CH;Ae97CC;EACE,YAAA;Efg8CH;Ael7CC;EACE,mBAAA;Efo7CH;Aer7CC;EACE,2BAAA;Efu7CH;Aex7CC;EACE,2BAAA;Ef07CH;Ae37CC;EACE,kBAAA;Ef67CH;Ae97CC;EACE,2BAAA;Efg8CH;Aej8CC;EACE,2BAAA;Efm8CH;Aep8CC;EACE,kBAAA;Efs8CH;Aev8CC;EACE,2BAAA;Efy8CH;Ae18CC;EACE,2BAAA;Ef48CH;Ae78CC;EACE,kBAAA;Ef+8CH;Aeh9CC;EACE,2BAAA;Efk9CH;Aen9CC;EACE,0BAAA;Efq9CH;Aet9CC;EACE,iBAAA;Efw9CH;Aa59CD;EE9BI;IACE,aAAA;If6/CH;Eet/CD;IACE,aAAA;Ifw/CD;Eez/CD;IACE,qBAAA;If2/CD;Ee5/CD;IACE,qBAAA;If8/CD;Ee//CD;IACE,YAAA;IfigDD;EelgDD;IACE,qBAAA;IfogDD;EergDD;IACE,qBAAA;IfugDD;EexgDD;IACE,YAAA;If0gDD;Ee3gDD;IACE,qBAAA;If6gDD;Ee9gDD;IACE,qBAAA;IfghDD;EejhDD;IACE,YAAA;IfmhDD;EephDD;IACE,qBAAA;IfshDD;EevhDD;IACE,oBAAA;IfyhDD;Ee3gDD;IACE,aAAA;If6gDD;Ee9gDD;IACE,qBAAA;IfghDD;EejhDD;IACE,qBAAA;IfmhDD;EephDD;IACE,YAAA;IfshDD;EevhDD;IACE,qBAAA;IfyhDD;Ee1hDD;IACE,qBAAA;If4hDD;Ee7hDD;IACE,YAAA;If+hDD;EehiDD;IACE,qBAAA;IfkiDD;EeniDD;IACE,qBAAA;IfqiDD;EetiDD;IACE,YAAA;IfwiDD;EeziDD;IACE,qBAAA;If2iDD;Ee5iDD;IACE,oBAAA;If8iDD;Ee1iDD;IACE,aAAA;If4iDD;Ee5jDD;IACE,YAAA;If8jDD;Ee/jDD;IACE,oBAAA;IfikDD;EelkDD;IACE,oBAAA;IfokDD;EerkDD;IACE,WAAA;IfukDD;EexkDD;IACE,oBAAA;If0kDD;Ee3kDD;IACE,oBAAA;If6kDD;Ee9kDD;IACE,WAAA;IfglDD;EejlDD;IACE,oBAAA;IfmlDD;EeplDD;IACE,oBAAA;IfslDD;EevlDD;IACE,WAAA;IfylDD;Ee1lDD;IACE,oBAAA;If4lDD;Ee7lDD;IACE,mBAAA;If+lDD;Ee3lDD;IACE,YAAA;If6lDD;Ee/kDD;IACE,mBAAA;IfilDD;EellDD;IACE,2BAAA;IfolDD;EerlDD;IACE,2BAAA;IfulDD;EexlDD;IACE,kBAAA;If0lDD;Ee3lDD;IACE,2BAAA;If6lDD;Ee9lDD;IACE,2BAAA;IfgmDD;EejmDD;IACE,kBAAA;IfmmDD;EepmDD;IACE,2BAAA;IfsmDD;EevmDD;IACE,2BAAA;IfymDD;Ee1mDD;IACE,kBAAA;If4mDD;Ee7mDD;IACE,2BAAA;If+mDD;EehnDD;IACE,0BAAA;IfknDD;EennDD;IACE,iBAAA;IfqnDD;EACF;AajnDD;EEvCI;IACE,aAAA;If2pDH;EeppDD;IACE,aAAA;IfspDD;EevpDD;IACE,qBAAA;IfypDD;Ee1pDD;IACE,qBAAA;If4pDD;Ee7pDD;IACE,YAAA;If+pDD;EehqDD;IACE,qBAAA;IfkqDD;EenqDD;IACE,qBAAA;IfqqDD;EetqDD;IACE,YAAA;IfwqDD;EezqDD;IACE,qBAAA;If2qDD;Ee5qDD;IACE,qBAAA;If8qDD;Ee/qDD;IACE,YAAA;IfirDD;EelrDD;IACE,qBAAA;IforDD;EerrDD;IACE,oBAAA;IfurDD;EezqDD;IACE,aAAA;If2qDD;Ee5qDD;IACE,qBAAA;If8qDD;Ee/qDD;IACE,qBAAA;IfirDD;EelrDD;IACE,YAAA;IforDD;EerrDD;IACE,qBAAA;IfurDD;EexrDD;IACE,qBAAA;If0rDD;Ee3rDD;IACE,YAAA;If6rDD;Ee9rDD;IACE,qBAAA;IfgsDD;EejsDD;IACE,qBAAA;IfmsDD;EepsDD;IACE,YAAA;IfssDD;EevsDD;IACE,qBAAA;IfysDD;Ee1sDD;IACE,oBAAA;If4sDD;EexsDD;IACE,aAAA;If0sDD;Ee1tDD;IACE,YAAA;If4tDD;Ee7tDD;IACE,oBAAA;If+tDD;EehuDD;IACE,oBAAA;IfkuDD;EenuDD;IACE,WAAA;IfquDD;EetuDD;IACE,oBAAA;IfwuDD;EezuDD;IACE,oBAAA;If2uDD;Ee5uDD;IACE,WAAA;If8uDD;Ee/uDD;IACE,oBAAA;IfivDD;EelvDD;IACE,oBAAA;IfovDD;EervDD;IACE,WAAA;IfuvDD;EexvDD;IACE,oBAAA;If0vDD;Ee3vDD;IACE,mBAAA;If6vDD;EezvDD;IACE,YAAA;If2vDD;Ee7uDD;IACE,mBAAA;If+uDD;EehvDD;IACE,2BAAA;IfkvDD;EenvDD;IACE,2BAAA;IfqvDD;EetvDD;IACE,kBAAA;IfwvDD;EezvDD;IACE,2BAAA;If2vDD;Ee5vDD;IACE,2BAAA;If8vDD;Ee/vDD;IACE,kBAAA;IfiwDD;EelwDD;IACE,2BAAA;IfowDD;EerwDD;IACE,2BAAA;IfuwDD;EexwDD;IACE,kBAAA;If0wDD;Ee3wDD;IACE,2BAAA;If6wDD;Ee9wDD;IACE,0BAAA;IfgxDD;EejxDD;IACE,iBAAA;IfmxDD;EACF;AaxwDD;EE9CI;IACE,aAAA;IfyzDH;EelzDD;IACE,aAAA;IfozDD;EerzDD;IACE,qBAAA;IfuzDD;EexzDD;IACE,qBAAA;If0zDD;Ee3zDD;IACE,YAAA;If6zDD;Ee9zDD;IACE,qBAAA;Ifg0DD;Eej0DD;IACE,qBAAA;Ifm0DD;Eep0DD;IACE,YAAA;Ifs0DD;Eev0DD;IACE,qBAAA;Ify0DD;Ee10DD;IACE,qBAAA;If40DD;Ee70DD;IACE,YAAA;If+0DD;Eeh1DD;IACE,qBAAA;Ifk1DD;Een1DD;IACE,oBAAA;Ifq1DD;Eev0DD;IACE,aAAA;Ify0DD;Ee10DD;IACE,qBAAA;If40DD;Ee70DD;IACE,qBAAA;If+0DD;Eeh1DD;IACE,YAAA;Ifk1DD;Een1DD;IACE,qBAAA;Ifq1DD;Eet1DD;IACE,qBAAA;Ifw1DD;Eez1DD;IACE,YAAA;If21DD;Ee51DD;IACE,qBAAA;If81DD;Ee/1DD;IACE,qBAAA;Ifi2DD;Eel2DD;IACE,YAAA;Ifo2DD;Eer2DD;IACE,qBAAA;Ifu2DD;Eex2DD;IACE,oBAAA;If02DD;Eet2DD;IACE,aAAA;Ifw2DD;Eex3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AAA;EACI,uBAAA;ErBqsFT;AoB9qFD;EClBI,gBAAA;EACA,2BAAA;ErBmsFH;AoB/qFD;ECrDE,gBAAA;EACA,2BAAA;EACA,uBAAA;ErBuuFD;AqBruFC;;;;;EAKE,gBAAA;EACA,2BAAA;EACI,uBAAA;ErBuuFP;AqBruFC;;;EAGE,wBAAA;ErBuuFH;AqBluFG;;;;;;;;;;;;;;;EAKE,2BAAA;EACI,uBAAA;ErB8uFT;AoBptFD;ECrBI,gBAAA;EACA,2BAAA;ErB4uFH;AoBptFD;ECzDE,gBAAA;EACA,2BAAA;EACA,uBAAA;ErBgxFD;AqB9wFC;;;;;EAKE,gBAAA;EACA,2BAAA;EACI,uBAAA;ErBgxFP;AqB9wFC;;;EAGE,wBAAA;ErBgxFH;AqB3wFG;;;;;;;;;;;;;;;EAKE,2BAAA;EACI,uBAAA;ErBuxFT;AoBzvFD;ECzBI,gBAAA;EACA,2BAAA;ErBqxFH;AoBzvFD;EC7DE,gBAAA;EACA,2BAAA;EACA,uBAAA;ErByzFD;AqBvzFC;;;;;EAKE,gBAAA;EACA,2BAAA;EACI,uBAAA;ErByzFP;AqBvzFC;;;EAGE,wBAAA;ErByzFH;AqBpzFG;;;;;;;;;;;;;;;EAKE,2BAAA;EACI,uBAAA;ErBg0FT;AoB9xFD;EC7BI,gBAAA;EACA,2BAAA;ErB8zFH;AoB9xFD;ECjEE,gBAAA;EACA,2BAAA;EACA,uBAAA;ErBk2FD;AqBh2FC;;;;;EAKE,gBAAA;EACA,2BAAA;EACI,uBAAA;ErBk2FP;AqBh2FC;;;EAGE,wBAAA;ErBk2FH;AqB71FG;;;;;;;;;;;;;;;EAKE,2BAAA;EACI,uBAAA;ErBy2FT;AoBn0FD;ECjCI,gBAAA;EACA,2BAAA;ErBu2FH;AoBn0FD;ECrEE,gBAAA;EACA,2BAAA;EACA,uBAAA;ErB24FD;AqBz4FC;;;;;EAKE,gBAAA;EACA,2BAAA;EACI,uBAAA;ErB24FP;AqBz4FC;;;EAGE,wBAAA;ErB24FH;AqBt4FG;;;;;;;;;;;;;;;EAKE,2BAAA;EACI,uBAAA;ErBk5FT;AoBx2FD;ECrCI,gBAAA;EACA,2BAAA;ErBg5FH;AoBn2FD;EACE,gBAAA;EACA,qBAAA;EACA,iBAAA;EACA,kBAAA;EpBq2FD;AoBn2FC;;;;EAIE,+BAAA;Ef1BF,0BAAA;EACQ,kBAAA;ELg4FT;AoBp2FC;;;;EAIE,2BAAA;EpBs2FH;AoBp2FC;;EAEE,gBAAA;EACA,4BAAA;EACA,+BAAA;EpBs2FH;AoBl2FG;;;;EAEE,gBAAA;EACA,uBAAA;EpBs2FL;AoB71FD;;EC9EE,oBAAA;EACA,iBAAA;EACA,mBAAA;EACA,oBAAA;ErB+6FD;AoBh2FD;;EClFE,mBAAA;EACA,iBAAA;EACA,kBAAA;EACA,oBAAA;ErBs7FD;AoBn2FD;;ECtFE,kBAAA;EACA,iBAAA;EACA,kBAAA;EACA,oBAAA;ErB67FD;AoBl2FD;EACE,gBAAA;EACA,aAAA;EpBo2FD;AoBh2FD;EACE,iBAAA;EpBk2FD;AoB31FC;;;EACE,aAAA;EpB+1FH;AuBh/FD;EACE,YAAA;ElBiLA,0CAAA;EACK,qCAAA;EACG,kCAAA;ELk0FT;AuBn/FC;EACE,YAAA;EvBq/FH;AuBj/FD;EACE,eAAA;EvBm/FD;AuBj/FC;EAAY,gBAAA;EvBo/Fb;AuBn/FC;EAAY,oBAAA;EvBs/Fb;AuBr/FC;EAAY,0BAAA;EvBw/Fb;AuBr/FD;EACE,oBAAA;EACA,WAAA;EACA,kBAAA;ElB+JA,uCAAA;EACK,kCAAA;EACG,+BAAA;ELy1FT;AwBhhGD;EACE,uBAAA;EACA,UAAA;EACA,WAAA;EACA,kBAAA;EACA,wBAAA;EACA,uBAAA;EACA,qCAAA;EACA,oCAAA;ExBkhGD;AwB9gGD;EACE,oBAAA;ExBghGD;AwB5gGD;EACE,YAAA;ExB8gGD;AwB1gGD;EACE,oBAAA;EACA,WAAA;EACA,SAAA;EACA,eAAA;EACA,eAAA;EACA,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,kBAAA;EACA,iBAAA;EACA,kBAAA;EACA,2BAAA;EACA,2BAAA;EACA,uCAAA;EACA,oBAAA;EnBwBA,qDAAA;EACQ,6CAAA;EmBvBR,sCAAA;EAAA,8BAAA;ExB6gGD;AwBxgGC;EACE,UAAA;EACA,YAAA;ExB0gGH;AwBniGD;ECvBE,aAAA;EACA,eAAA;EACA,kBAAA;EACA,2BAAA;EzB6jGD;AwBziGD;EAmCI,gBAAA;EACA,mBAAA;EACA,aAAA;EACA,qBAAA;EACA,yBAAA;EACA,gBAAA;EACA,qBAAA;ExBygGH;AwBngGC;;EAEE,uBAAA;EACA,gBAAA;EACA,2BAAA;ExBqgGH;AwB//FC;;;EAGE,gBAAA;EACA,uBAAA;EACA,YAAA;EACA,2BAAA;ExBigGH;AwBx/FC;;;EAGE,gBAAA;ExB0/FH;AwBr/FC;;EAEE,uBAAA;EACA,+BAAA;EACA,wBAAA;EE1GF,qEAAA;EF4GE,qBAAA;ExBu/FH;AwBl/FD;EAGI,gBAAA;ExBk/FH;AwBr/FD;EAQI,YAAA;ExBg/FH;AwBx+FD;EACE,YAAA;EACA,UAAA;ExB0+FD;AwBl+FD;EACE,SAAA;EACA,aAAA;ExBo+FD;AwBh+FD;EACE,gBAAA;EACA,mBAAA;EACA,iBAAA;EACA,yBAAA;EACA,gBAAA;EACA,qBAAA;ExBk+FD;AwB99FD;EACE,iBAAA;EACA,SAAA;EACA,UAAA;EACA,WAAA;EACA,QAAA;EACA,cAAA;ExBg+FD;AwB59FD;EACE,UAAA;EACA,YAAA;ExB89FD;AwBt9FD;;EAII,eAAA;EACA,0BAAA;EACA,aAAA;ExBs9FH;AwB59FD;;EAUI,WAAA;EACA,cAAA;EACA,oBAAA;ExBs9FH;AwBh8FD;EAZE;IAnEA,YAAA;IACA,UAAA;IxBmhGC;EwBj9FD;IAzDA,SAAA;IACA,aAAA;IxB6gGC;EACF;A2B5pGD;;EAEE,oBAAA;EACA,uBAAA;EACA,wBAAA;E3B8pGD;A2BlqGD;;EAMI,oBAAA;EACA,aAAA;E3BgqGH;A2B9pGG;;;;;;;;EAIE,YAAA;E3BoqGL;A2BlqGG;;EAEE,YAAA;E3BoqGL;A2B9pGD;;;;EAKI,mBAAA;E3B+pGH;A2B1pGD;EACE,mBAAA;E3B4pGD;A2B7pGD;;EAMI,aAAA;E3B2pGH;A2BjqGD;;;EAWI,kBAAA;E3B2pGH;A2BvpGD;EACE,kBAAA;E3BypGD;A2BrpGD;EACE,gBAAA;E3BupGD;A2BtpGC;ECrDA,+BAAA;EACG,4BAAA;E5B8sGJ;A2BrpGD;;EClDE,8BAAA;EACG,2BAAA;E5B2sGJ;A2BppGD;EACE,aAAA;E3BspGD;A2BppGD;EACE,kBAAA;E3BspGD;A2BppGD;;ECtEE,+BAAA;EACG,4BAAA;E5B8tGJ;A2BnpGD;ECpEE,8BAAA;EACG,2BAAA;E5B0tGJ;A2BlpGD;;EAEE,YAAA;E3BopGD;A2BnoGD;EACE,mBAAA;EACA,oBAAA;E3BqoGD;A2BnoGD;EACE,oBAAA;EACA,qBAAA;E3BqoGD;A2BhoGD;EtBlDE,0DAAA;EACQ,kDAAA;ELqrGT;A2BhoGC;EtBtDA,0BAAA;EACQ,kBAAA;ELyrGT;A2B7nGD;EACE,gBAAA;E3B+nGD;A2B5nGD;EACE,yBAAA;EACA,wBAAA;E3B8nGD;A2B3nGD;EACE,yBAAA;E3B6nGD;A2BtnGD;;;EAII,gBAAA;EACA,aAAA;EACA,aAAA;EACA,iBAAA;E3BunGH;A2B9nGD;EAcM,aAAA;E3BmnGL;A2BjoGD;;;;EAsBI,kBAAA;EACA,gBAAA;E3BinGH;A2B5mGC;EACE,kBAAA;E3B8mGH;A2B5mGC;EACE,8BAAA;ECvKF,+BAAA;EACC,8BAAA;E5BsxGF;A2B7mGC;EACE,gCAAA;ECnLF,4BAAA;EACC,2BAAA;E5BmyGF;A2B7mGD;EACE,kBAAA;E3B+mGD;A2B7mGD;;EClLE,+BAAA;EACC,8BAAA;E5BmyGF;A2B5mGD;EChME,4BAAA;EACC,2BAAA;E5B+yGF;A2BvmGD;EACE,gBAAA;EACA,aAAA;EACA,qBAAA;EACA,2BAAA;E3BymGD;A2B7mGD;;EAOI,aAAA;EACA,qBAAA;EACA,WAAA;E3B0mGH;A2BnnGD;EAYI,aAAA;E3B0mGH;A2BtnGD;EAgBI,YAAA;E3BymGH;A2B3lGD;;EAEE,oBAAA;EACA,aAAA;EL1OA,YAAA;EAGA,0BAAA;EtBs0GD;A6Bt0GD;EACE,oBAAA;EACA,gBAAA;EACA,2BAAA;E7Bw0GD;A6Br0GC;EACE,aAAA;EACA,iBAAA;EACA,kBAAA;E7Bu0GH;A6Bh1GD;EAeI,oBAAA;EACA,YAAA;EAKA,aAAA;EAEA,aAAA;EACA,kBAAA;E7B+zGH;A6BtzGD;;;EV0BE,cAAA;EACA,oBAAA;EACA,iBAAA;EACA,mBAAA;EACA,oBAAA;EnBiyGD;AmB/xGC;;;EACE,cAAA;EACA,mBAAA;EnBmyGH;AmBhyGC;;;;;;EAEE,cAAA;EnBsyGH;A6Bx0GD;;;EVqBE,cAAA;EACA,mBAAA;EACA,iBAAA;EACA,kBAAA;EACA,oBAAA;EnBwzGD;AmBtzGC;;;EACE,cAAA;EACA,mBAAA;EnB0zGH;AmBvzGC;;;;;;EAEE,cAAA;EnB6zGH;A6Bt1GD;;;EAGE,qBAAA;E7Bw1GD;A6Bt1GC;;;EACE,kBAAA;E7B01GH;A6Bt1GD;;EAEE,WAAA;EACA,qBAAA;EACA,wBAAA;E7Bw1GD;A6Bn1GD;EACE,mBAAA;EACA,iBAAA;EACA,qBAAA;EACA,gBAAA;EACA,gBAAA;EACA,oBAAA;EACA,2BAAA;EACA,2BAAA;EACA,oBAAA;E7Bq1GD;A6Bl1GC;EACE,mBAAA;EACA,iBAAA;EACA,oBAAA;E7Bo1GH;A6Bl1GC;EACE,oBAAA;EACA,iBAAA;EACA,oBAAA;E7Bo1GH;A6Bx2GD;;EA0BI,eAAA;E7Bk1GH;A6B70GD;;;;;;;EDhGE,+BAAA;EACG,4BAAA;E5Bs7GJ;A6B90GD;EACE,iBAAA;E7Bg1GD;A6B90GD;;;;;;;EDpGE,8BAAA;EACG,2BAAA;E5B27GJ;A6B/0GD;EACE,gBAAA;E7Bi1GD;A6B50GD;EACE,oBAAA;EAGA,cAAA;EACA,qBAAA;E7B40GD;A6Bj1GD;EAUI,oBAAA;E7B00GH;A6Bp1GD;EAYM,mBAAA;E7B20GL;A6Bx0GG;;;EAGE,YAAA;E7B00GL;A6Br0GC;;EAGI,oBAAA;E7Bs0GL;A6Bn0GC;;EAGI,mBAAA;E7Bo0GL;A8B99GD;EACE,kBAAA;EACA,iBAAA;EACA,kBAAA;E9Bg+GD;A8Bn+GD;EAOI,oBAAA;EACA,gBAAA;E9B+9GH;A8Bv+GD;EAWM,oBAAA;EACA,gBAAA;EACA,oBAAA;E9B+9GL;A8B99GK;;EAEE,uBAAA;EACA,2BAAA;E9Bg+GP;A8B39GG;EACE,gBAAA;E9B69GL;A8B39GK;;EAEE,gBAAA;EACA,uBAAA;EACA,+BAAA;EACA,qBAAA;E9B69GP;A8Bt9GG;;;EAGE,2BAAA;EACA,uBAAA;E9Bw9GL;A8BjgHD;ELHE,aAAA;EACA,eAAA;EACA,kBAAA;EACA,2BAAA;EzBugHD;A8BvgHD;EA0DI,iBAAA;E9Bg9GH;A8Bv8GD;EACE,kCAAA;E9By8GD;A8B18GD;EAGI,aAAA;EAEA,qBAAA;E9By8GH;A8B98GD;EASM,mBAAA;EACA,yBAAA;EACA,+BAAA;EACA,4BAAA;E9Bw8GL;A8Bv8GK;EACE,uCAAA;E9By8GP;A8Bn8GK;;;EAGE,gBAAA;EACA,2BAAA;EACA,2BAAA;EACA,kCAAA;EACA,iBAAA;E9Bq8GP;A8Bh8GC;EAqDA,aAAA;EA8BA,kBAAA;E9Bi3GD;A8Bp8GC;EAwDE,aAAA;E9B+4GH;A8Bv8GC;EA0DI,oBAAA;EACA,oBAAA;E9Bg5GL;A8B38GC;EAgEE,WAAA;EACA,YAAA;E9B84GH;A8Bl4GD;EAAA;IAPM,qBAAA;IACA,WAAA;I9B64GH;E8Bv4GH;IAJQ,kBAAA;I9B84GL;EACF;A8Bx9GC;EAuFE,iBAAA;EACA,oBAAA;E9Bo4GH;A8B59GC;;;EA8FE,2BAAA;E9Bm4GH;A8Br3GD;EAAA;IATM,kCAAA;IACA,4BAAA;I9Bk4GH;E8B13GH;;;IAHM,8BAAA;I9Bk4GH;EACF;A8Bn+GD;EAEI,aAAA;E9Bo+GH;A8Bt+GD;EAMM,oBAAA;E9Bm+GL;A8Bz+GD;EASM,kBAAA;E9Bm+GL;A8B99GK;;;EAGE,gBAAA;EACA,2BAAA;E9Bg+GP;A8Bx9GD;EAEI,aAAA;E9By9GH;A8B39GD;EAIM,iBAAA;EACA,gBAAA;E9B09GL;A8B98GD;EACE,aAAA;E9Bg9GD;A8Bj9GD;EAII,aAAA;E9Bg9GH;A8Bp9GD;EAMM,oBAAA;EACA,oBAAA;E9Bi9GL;A8Bx9GD;EAYI,WAAA;EACA,YAAA;E9B+8GH;A8Bn8GD;EAAA;IAPM,qBAAA;IACA,WAAA;I9B88GH;E8Bx8GH;IAJQ,kBAAA;I9B+8GL;EACF;A8Bv8GD;EACE,kBAAA;E9By8GD;A8B18GD;EAKI,iBAAA;EACA,oBAAA;E9Bw8GH;A8B98GD;;;EAYI,2BAAA;E9Bu8GH;A8Bz7GD;EAAA;IATM,kCAAA;IACA,4BAAA;I9Bs8GH;E8B97GH;;;IAHM,8BAAA;I9Bs8GH;EACF;A8B77GD;EAEI,eAAA;E9B87GH;A8Bh8GD;EAKI,gBAAA;E9B87GH;A8Br7GD;EAEE,kBAAA;EF3OA,4BAAA;EACC,2BAAA;E5BkqHF;A+B5pHD;EACE,oBAAA;EACA,kBAAA;EACA,qBAAA;EACA,+BAAA;E/B8pHD;A+BtpHD;EAAA;IAFI,oBAAA;I/B4pHD;EACF;A+B7oHD;EAAA;IAFI,aAAA;I/BmpHD;EACF;A+BroHD;EACE,qBAAA;EACA,qBAAA;EACA,oBAAA;EACA,mCAAA;EACA,4DAAA;EAAA,oDAAA;EAEA,mCAAA;E/BsoHD;A+BpoHC;EACE,kBAAA;E/BsoHH;A+B1mHD;EAAA;IAxBI,aAAA;IACA,eAAA;IACA,0BAAA;IAAA,kBAAA;I/BsoHD;E+BpoHC;IACE,2BAAA;IACA,yBAAA;IACA,mBAAA;IACA,8BAAA;I/BsoHH;E+BnoHC;IACE,qBAAA;I/BqoHH;E+BhoHC;;;IAGE,iBAAA;IACA,kBAAA;I/BkoHH;EACF;A+B9nHD;;EAGI,mBAAA;E/B+nHH;A+B1nHC;EAAA;;IAFI,mBAAA;I/BioHH;EACF;A+BxnHD;;;;EAII,qBAAA;EACA,oBAAA;E/B0nHH;A+BpnHC;EAAA;;;;IAHI,iBAAA;IACA,gBAAA;I/B8nHH;EACF;A+BlnHD;EACE,eAAA;EACA,uBAAA;E/BonHD;A+B/mHD;EAAA;IAFI,kBAAA;I/BqnHD;EACF;A+BjnHD;;EAEE,iBAAA;EACA,UAAA;EACA,SAAA;EACA,eAAA;E1BGA,yCAAA;EACQ,oCAAA;EAAA,iCAAA;ELinHT;A+B9mHD;EAAA;;IAFI,kBAAA;I/BqnHD;EACF;A+BnnHD;EACE,QAAA;EACA,uBAAA;E/BqnHD;A+BnnHD;EACE,WAAA;EACA,kBAAA;EACA,uBAAA;E/BqnHD;A+B/mHD;EACE,aAAA;EACA,oBAAA;EACA,iBAAA;EACA,mBAAA;EACA,cAAA;E/BinHD;A+B/mHC;;EAEE,uBAAA;E/BinHH;A+BxmHD;EALI;;IAEE,oBAAA;I/BgnHH;EACF;A+BtmHD;EACE,oBAAA;EACA,cAAA;EACA,oBAAA;EACA,mBAAA;EC3LA,iBAAA;EACA,oBAAA;ED4LA,+BAAA;EACA,wBAAA;EACA,+BAAA;EACA,oBAAA;E/BymHD;A+BrmHC;EACE,YAAA;E/BumHH;A+BrnHD;EAmBI,gBAAA;EACA,aAAA;EACA,aAAA;EACA,oBAAA;E/BqmHH;A+B3nHD;EAyBI,iBAAA;E/BqmHH;A+B/lHD;EAAA;IAFI,eAAA;I/BqmHD;EACF;A+B5lHD;EACE,qBAAA;E/B8lHD;A+B/lHD;EAII,mBAAA;EACA,sBAAA;EACA,mBAAA;E/B8lHH;A+BnkHC;EAAA;IArBI,kBAAA;IACA,aAAA;IACA,aAAA;IACA,eAAA;IACA,+BAAA;IACA,WAAA;IACA,0BAAA;IAAA,kBAAA;I/B4lHH;E+B7kHD;;IAZM,4BAAA;I/B6lHL;E+BjlHD;IATM,mBAAA;I/B6lHL;E+B5lHK;;IAEE,wBAAA;I/B8lHP;EACF;A+BxkHD;EAAA;IAfI,aAAA;IACA,WAAA;I/B2lHD;E+B7kHH;IAXM,aAAA;I/B2lHH;E+BhlHH;IATQ,mBAAA;IACA,sBAAA;I/B4lHL;E+BxlHC;IACE,qBAAA;I/B0lHH;EACF;A+BzkHD;EALE;IE9QA,wBAAA;IjCg2HC;E+BjlHD;IElRA,yBAAA;IjCs2HC;EACF;A+B5kHD;EACE,oBAAA;EACA,qBAAA;EACA,oBAAA;EACA,mCAAA;EACA,sCAAA;E1B3OA,8FAAA;EACQ,sFAAA;E2B/DR,iBAAA;EACA,oBAAA;EhC03HD;AkBl7GD;EAAA;IA7DM,uBAAA;IACA,kBAAA;IACA,wBAAA;IlBm/GH;EkBx7GH;IAtDM,uBAAA;IACA,aAAA;IACA,wBAAA;IlBi/GH;EkB77GH;IAhDM,uBAAA;IACA,wBAAA;IlBg/GH;EkBj8GH;;;IA1CQ,aAAA;IlBg/GL;EkBt8GH;IApCM,aAAA;IlB6+GH;EkBz8GH;IAhCM,kBAAA;IACA,wBAAA;IlB4+GH;EkB78GH;;IAvBM,uBAAA;IACA,eAAA;IACA,kBAAA;IACA,wBAAA;IlBw+GH;EkBp9GH;;IAjBQ,iBAAA;IlBy+GL;EkBx9GH;;IAZM,oBAAA;IACA,gBAAA;IlBw+GH;EkB79GH;IAHM,QAAA;IlBm+GH;EACF;A+BtnHC;EAAA;IAFI,oBAAA;I/B4nHH;EACF;A+BvmHD;EAAA;IAbI,aAAA;IACA,WAAA;IACA,gBAAA;IACA,iBAAA;IACA,gBAAA;IACA,mBAAA;I1BlQF,0BAAA;IACQ,kBAAA;IL23HP;E+BtnHC;IACE,qBAAA;I/BwnHH;EACF;A+BhnHD;EACE,eAAA;EHlVA,4BAAA;EACC,2BAAA;E5Bq8HF;A+BhnHD;EH9UE,+BAAA;EACC,8BAAA;E5Bi8HF;A+B3mHD;EC5VE,iBAAA;EACA,oBAAA;EhC08HD;A+B5mHC;EC/VA,kBAAA;EACA,qBAAA;EhC88HD;A+B7mHC;EClWA,kBAAA;EACA,qBAAA;EhCk9HD;A+BvmHD;EC5WE,kBAAA;EACA,qBAAA;EhCs9HD;A+B9lHD;EAAA;IATI,aAAA;IACA,mBAAA;IACA,oBAAA;I/B2mHD;E+BxmHC;IACE,iBAAA;I/B0mHH;EACF;A+BlmHD;EACE,2BAAA;EACA,uBAAA;E/BomHD;A+BtmHD;EAKI,gBAAA;E/BomHH;A+BnmHG;;EAEE,gBAAA;EACA,+BAAA;E/BqmHL;A+B9mHD;EAcI,gBAAA;E/BmmHH;A+BjnHD;EAmBM,gBAAA;E/BimHL;A+B/lHK;;EAEE,gBAAA;EACA,+BAAA;E/BimHP;A+B7lHK;;;EAGE,gBAAA;EACA,2BAAA;E/B+lHP;A+B3lHK;;;EAGE,gBAAA;EACA,+BAAA;E/B6lHP;A+BroHD;EA8CI,uBAAA;E/B0lHH;A+BzlHG;;EAEE,2BAAA;E/B2lHL;A+B5oHD;EAoDM,2BAAA;E/B2lHL;A+B/oHD;;EA0DI,uBAAA;E/BylHH;A+BllHK;;;EAGE,2BAAA;EACA,gBAAA;E/BolHP;A+BnjHC;EAAA;IAzBQ,gBAAA;I/BglHP;E+B/kHO;;IAEE,gBAAA;IACA,+BAAA;I/BilHT;E+B7kHO;;;IAGE,gBAAA;IACA,2BAAA;I/B+kHT;E+B3kHO;;;IAGE,gBAAA;IACA,+BAAA;I/B6kHT;EACF;A+B/qHD;EA8GI,gBAAA;E/BokHH;A+BnkHG;EACE,gBAAA;E/BqkHL;A+BrrHD;EAqHI,gBAAA;E/BmkHH;A+BlkHG;;EAEE,gBAAA;E/BokHL;A+BhkHK;;;;EAEE,gBAAA;E/BokHP;A+B5jHD;EACE,2BAAA;EACA,uBAAA;E/B8jHD;A+BhkHD;EAKI,gBAAA;E/B8jHH;A+B7jHG;;EAEE,gBAAA;EACA,+BAAA;E/B+jHL;A+BxkHD;EAcI,gBAAA;E/B6jHH;A+B3kHD;EAmBM,gBAAA;E/B2jHL;A+BzjHK;;EAEE,gBAAA;EACA,+BAAA;E/B2jHP;A+BvjHK;;;EAGE,gBAAA;EACA,2BAAA;E/ByjHP;A+BrjHK;;;EAGE,gBAAA;EACA,+BAAA;E/BujHP;A+B/lHD;EA+CI,uBAAA;E/BmjHH;A+BljHG;;EAEE,2BAAA;E/BojHL;A+BtmHD;EAqDM,2BAAA;E/BojHL;A+BzmHD;;EA2DI,uBAAA;E/BkjHH;A+B5iHK;;;EAGE,2BAAA;EACA,gBAAA;E/B8iHP;A+BvgHC;EAAA;IA/BQ,uBAAA;I/B0iHP;E+B3gHD;IA5BQ,2BAAA;I/B0iHP;E+B9gHD;IAzBQ,gBAAA;I/B0iHP;E+BziHO;;IAEE,gBAAA;IACA,+BAAA;I/B2iHT;E+BviHO;;;IAGE,gBAAA;IACA,2BAAA;I/ByiHT;E+BriHO;;;IAGE,gBAAA;IACA,+BAAA;I/BuiHT;EACF;A+B/oHD;EA+GI,gBAAA;E/BmiHH;A+BliHG;EACE,gBAAA;E/BoiHL;A+BrpHD;EAsHI,gBAAA;E/BkiHH;A+BjiHG;;EAEE,gBAAA;E/BmiHL;A+B/hHK;;;;EAEE,gBAAA;E/BmiHP;AkCxqID;EACE,mBAAA;EACA,qBAAA;EACA,kBAAA;EACA,2BAAA;EACA,oBAAA;ElC0qID;AkC/qID;EAQI,uBAAA;ElC0qIH;AkClrID;EAWM,mBAAA;EACA,gBAAA;EACA,gBAAA;ElC0qIL;AkCvrID;EAkBI,gBAAA;ElCwqIH;AmC5rID;EACE,uBAAA;EACA,iBAAA;EACA,gBAAA;EACA,oBAAA;EnC8rID;AmClsID;EAOI,iBAAA;EnC8rIH;AmCrsID;;EAUM,oBAAA;EACA,aAAA;EACA,mBAAA;EACA,yBAAA;EACA,uBAAA;EACA,gBAAA;EACA,2BAAA;EACA,2BAAA;EACA,mBAAA;EnC+rIL;AmC7rIG;;EAGI,gBAAA;EPXN,gCAAA;EACG,6BAAA;E5B0sIJ;AmC5rIG;;EPvBF,iCAAA;EACG,8BAAA;E5ButIJ;AmCvrIG;;;;EAEE,gBAAA;EACA,2BAAA;EACA,uBAAA;EnC2rIL;AmCrrIG;;;;;;EAGE,YAAA;EACA,gBAAA;EACA,2BAAA;EACA,uBAAA;EACA,iBAAA;EnC0rIL;AmChvID;;;;;;EAiEM,gBAAA;EACA,2BAAA;EACA,uBAAA;EACA,qBAAA;EnCurIL;AmC9qID;;EC1EM,oBAAA;EACA,iBAAA;EpC4vIL;AoC1vIG;;ERMF,gCAAA;EACG,6BAAA;E5BwvIJ;AoCzvIG;;ERRF,iCAAA;EACG,8BAAA;E5BqwIJ;AmCxrID;;EC/EM,mBAAA;EACA,iBAAA;EpC2wIL;AoCzwIG;;ERMF,gCAAA;EACG,6BAAA;E5BuwIJ;AoCxwIG;;ERRF,iCAAA;EACG,8BAAA;E5BoxIJ;AqCvxID;EACE,iBAAA;EACA,gBAAA;EACA,kBAAA;EACA,oBAAA;ErCyxID;AqC7xID;EAOI,iBAAA;ErCyxIH;AqChyID;;EAUM,uBAAA;EACA,mBAAA;EACA,2BAAA;EACA,2BAAA;EACA,qBAAA;ErC0xIL;AqCxyID;;EAmBM,uBAAA;EACA,2BAAA;ErCyxIL;AqC7yID;;EA2BM,cAAA;ErCsxIL;AqCjzID;;EAkCM,aAAA;ErCmxIL;AqCrzID;;;;EA2CM,gBAAA;EACA,2BAAA;EACA,qBAAA;ErCgxIL;AsC9zID;EACE,iBAAA;EACA,yBAAA;EACA,gBAAA;EACA,mBAAA;EACA,gBAAA;EACA,gBAAA;EACA,oBAAA;EACA,qBAAA;EACA,0BAAA;EACA,sBAAA;EtCg0ID;AsC5zIG;;EAEE,gBAAA;EACA,uBAAA;EACA,iBAAA;EtC8zIL;AsCzzIC;EACE,eAAA;EtC2zIH;AsCvzIC;EACE,oBAAA;EACA,WAAA;EtCyzIH;AsClzID;ECtCE,2BAAA;EvC21ID;AuCx1IG;;EAEE,2BAAA;EvC01IL;AsCrzID;EC1CE,2BAAA;EvCk2ID;AuC/1IG;;EAEE,2BAAA;EvCi2IL;AsCxzID;EC9CE,2BAAA;EvCy2ID;AuCt2IG;;EAEE,2BAAA;EvCw2IL;AsC3zID;EClDE,2BAAA;EvCg3ID;AuC72IG;;EAEE,2BAAA;EvC+2IL;AsC9zID;ECtDE,2BAAA;EvCu3ID;AuCp3IG;;EAEE,2BAAA;EvCs3IL;AsCj0ID;EC1DE,2BAAA;EvC83ID;AuC33IG;;EAEE,2BAAA;EvC63IL;AwC/3ID;EACE,uBAAA;EACA,iBAAA;EACA,kBAAA;EACA,iBAAA;EACA,mBAAA;EACA,gBAAA;EACA,gBAAA;EACA,0BAAA;EACA,qBAAA;EACA,oBAAA;EACA,2BAAA;EACA,qBAAA;ExCi4ID;AwC93IC;EACE,eAAA;ExCg4IH;AwC53IC;EACE,oBAAA;EACA,WAAA;ExC83IH;AwC53IC;EACE,QAAA;EACA,kBAAA;ExC83IH;AwCz3IG;;EAEE,gBAAA;EACA,uBAAA;EACA,iBAAA;ExC23IL;AwCt3IC;;EAEE,gBAAA;EACA,2BAAA;ExCw3IH;AwCt3IC;EACE,kBAAA;ExCw3IH;AyCv6ID;EACE,eAAA;EACA,qBAAA;EACA,gBAAA;EACA,2BAAA;EzCy6ID;AyC76ID;;EAQI,gBAAA;EzCy6IH;AyCj7ID;EAWI,qBAAA;EACA,iBAAA;EACA,kBAAA;EzCy6IH;AyCt7ID;EAiBI,2BAAA;EzCw6IH;AyCr6IC;EACE,oBAAA;EzCu6IH;AyC57ID;EAyBI,iBAAA;EzCs6IH;AyCr5ID;EAAA;IAbI,mBAAA;IACA,sBAAA;IzCs6ID;EyCp6IC;IACE,oBAAA;IACA,qBAAA;IzCs6IH;EyC95IH;;IAHM,iBAAA;IzCq6IH;EACF;A0C58ID;EACE,gBAAA;EACA,cAAA;EACA,qBAAA;EACA,yBAAA;EACA,2BAAA;EACA,2BAAA;EACA,oBAAA;ErC8KA,0CAAA;EACK,qCAAA;EACG,kCAAA;ELiyIT;A0Cx9ID;;EAaI,mBAAA;EACA,oBAAA;E1C+8IH;A0C38IC;;;EAGE,uBAAA;E1C68IH;A0Cl+ID;EA0BI,cAAA;EACA,gBAAA;E1C28IH;A2Cp+ID;EACE,eAAA;EACA,qBAAA;EACA,+BAAA;EACA,oBAAA;E3Cs+ID;A2C1+ID;EAQI,eAAA;EAEA,gBAAA;E3Co+IH;A2C9+ID;EAcI,mBAAA;E3Cm+IH;A2Cj/ID;;EAoBI,kBAAA;E3Ci+IH;A2Cr/ID;EAuBI,iBAAA;E3Ci+IH;A2Cz9ID;;EAEE,qBAAA;E3C29ID;A2C79ID;;EAMI,oBAAA;EACA,WAAA;EACA,cAAA;EACA,gBAAA;E3C29IH;A2Cn9ID;ECrDE,2BAAA;EACA,uBAAA;EACA,gBAAA;E5C2gJD;A2Cx9ID;EChDI,2BAAA;E5C2gJH;A2C39ID;EC7CI,gBAAA;E5C2gJH;A2C39ID;ECxDE,2BAAA;EACA,uBAAA;EACA,gBAAA;E5CshJD;A2Ch+ID;ECnDI,2BAAA;E5CshJH;A2Cn+ID;EChDI,gBAAA;E5CshJH;A2Cn+ID;EC3DE,2BAAA;EACA,uBAAA;EACA,gBAAA;E5CiiJD;A2Cx+ID;ECtDI,2BAAA;E5CiiJH;A2C3+ID;ECnDI,gBAAA;E5CiiJH;A2C3+ID;EC9DE,2BAAA;EACA,uBAAA;EACA,gBAAA;E5C4iJD;A2Ch/ID;ECzDI,2BAAA;E5C4iJH;A2Cn/ID;ECtDI,gBAAA;E5C4iJH;A6C9iJD;EACE;IAAQ,6BAAA;I7CijJP;E6ChjJD;IAAQ,0BAAA;I7CmjJP;EACF;A6ChjJD;EACE;IAAQ,6BAAA;I7CmjJP;E6CljJD;IAAQ,0BAAA;I7CqjJP;EACF;A6CxjJD;EACE;IAAQ,6BAAA;I7CmjJP;E6CljJD;IAAQ,0BAAA;I7CqjJP;EACF;A6C7iJD;EACE,kBAAA;EACA,cAAA;EACA,qBAAA;EACA,2BAAA;EACA,oBAAA;ExCqCA,wDAAA;EACQ,gDAAA;EL2gJT;A6C5iJD;EACE,aAAA;EACA,WAAA;EACA,cAAA;EACA,iBAAA;EACA,mBAAA;EACA,gBAAA;EACA,oBAAA;EACA,2BAAA;ExCwBA,wDAAA;EACQ,gDAAA;EAsHR,qCAAA;EACK,gCAAA;EACG,6BAAA;ELk6IT;A6CziJD;;ECAI,+MAAA;EACA,0MAAA;EACA,uMAAA;EDCF,oCAAA;EAAA,4BAAA;E7C6iJD;A6CtiJD;;ExC7CE,4DAAA;EACK,uDAAA;EACG,oDAAA;ELulJT;A6CriJC;;EAEE,iBAAA;E7CuiJH;A6CpiJC;EACE,gBAAA;EACA,iBAAA;EACA,+BAAA;EACA,wBAAA;EACA,0BAAA;EAAA,kBAAA;E7CsiJH;A6C7hJD;EEvFE,2BAAA;E/CunJD;A+CpnJC;EDgDE,+MAAA;EACA,0MAAA;EACA,uMAAA;E9CukJH;A6CjiJD;EE3FE,2BAAA;E/C+nJD;A+C5nJC;EDgDE,+MAAA;EACA,0MAAA;EACA,uMAAA;E9C+kJH;A6CriJD;EE/FE,2BAAA;E/CuoJD;A+CpoJC;EDgDE,+MAAA;EACA,0MAAA;EACA,uMAAA;E9CulJH;A6CziJD;EEnGE,2BAAA;E/C+oJD;A+C5oJC;EDgDE,+MAAA;EACA,0MAAA;EACA,uMAAA;E9C+lJH;AgD9oJD;;EAEE,kBAAA;EACA,SAAA;EhDgpJD;AgD5oJD;;EAEE,kBAAA;EhD8oJD;AgD5oJD;EACE,eAAA;EhD8oJD;AgD1oJD;EACE,gBAAA;EhD4oJD;AgDxoJD;EACE,iBAAA;EhD0oJD;AgDnoJD;EAEI,oBAAA;EhDooJH;AgDtoJD;EAKI,mBAAA;EhDooJH;AgD3nJD;EACE,iBAAA;EACA,kBAAA;EhD6nJD;AiD1qJD;EAEE,qBAAA;EACA,iBAAA;EjD2qJD;AiDnqJD;EACE,oBAAA;EACA,gBAAA;EACA,oBAAA;EAEA,qBAAA;EACA,2BAAA;EACA,2BAAA;EjDoqJD;AiDjqJC;ErB3BA,8BAAA;EACC,6BAAA;E5B+rJF;AiDlqJC;EACE,kBAAA;ErBvBF,iCAAA;EACC,gCAAA;E5B4rJF;AiDprJD;EAoBI,cAAA;EjDmqJH;AiDvrJD;EAuBI,mBAAA;EjDmqJH;AiDzpJD;EACE,gBAAA;EjD2pJD;AiD5pJD;EAII,gBAAA;EjD2pJH;AiDvpJC;;EAEE,uBAAA;EACA,gBAAA;EACA,2BAAA;EjDypJH;AiDnpJC;;;EAGE,2BAAA;EACA,gBAAA;EjDqpJH;AiDzpJC;;;EAQI,gBAAA;EjDspJL;AiD9pJC;;;EAWI,gBAAA;EjDwpJL;AiDnpJC;;;EAGE,YAAA;EACA,gBAAA;EACA,2BAAA;EACA,uBAAA;EjDqpJH;AiD3pJC;;;;;;;;;EAYI,gBAAA;EjD0pJL;AiDtqJC;;;EAeI,gBAAA;EjD4pJL;AkD/vJC;EACE,gBAAA;EACA,2BAAA;ElDiwJH;AkD/vJG;EACE,gBAAA;ElDiwJL;AkDlwJG;EAII,gBAAA;ElDiwJP;AkD9vJK;;EAEE,gBAAA;EACA,2BAAA;ElDgwJP;AkD9vJK;;;EAGE,aAAA;EACA,2BAAA;EACA,uBAAA;ElDgwJP;AkDrxJC;EACE,gBAAA;EACA,2BAAA;ElDuxJH;AkDrxJG;EACE,gBAAA;ElDuxJL;AkDxxJG;EAII,gBAAA;ElDuxJP;AkDpxJK;;EAEE,gBAAA;EACA,2BAAA;ElDsxJP;AkDpxJK;;;EAGE,aAAA;EACA,2BAAA;EACA,uBAAA;ElDsxJP;AkD3yJC;EACE,gBAAA;EACA,2BAAA;ElD6yJH;AkD3yJG;EACE,gBAAA;ElD6yJL;AkD9yJG;EAII,gBAAA;ElD6yJP;AkD1yJK;;EAEE,gBAAA;EACA,2BAAA;ElD4yJP;AkD1yJK;;;EAGE,aAAA;EACA,2BAAA;EACA,uBAAA;ElD4yJP;AkDj0JC;EACE,gBAAA;EACA,2BAAA;ElDm0JH;AkDj0JG;EACE,gBAAA;ElDm0JL;AkDp0JG;EAII,gBAAA;ElDm0JP;AkDh0JK;;EAEE,gBAAA;EACA,2BAAA;ElDk0JP;AkDh0JK;;;EAGE,aAAA;EACA,2BAAA;EACA,uBAAA;ElDk0JP;AiD/tJD;EACE,eAAA;EACA,oBAAA;EjDiuJD;AiD/tJD;EACE,kBAAA;EACA,kBAAA;EjDiuJD;AmD51JD;EACE,qBAAA;EACA,2BAAA;EACA,+BAAA;EACA,oBAAA;E9C0DA,mDAAA;EACQ,2CAAA;ELqyJT;AmD31JD;EACE,eAAA;EnD61JD;AmDx1JD;EACE,oBAAA;EACA,sCAAA;EvBpBA,8BAAA;EACC,6BAAA;E5B+2JF;AmD91JD;EAMI,gBAAA;EnD21JH;AmDt1JD;EACE,eAAA;EACA,kBAAA;EACA,iBAAA;EACA,gBAAA;EnDw1JD;AmD51JD;EAOI,gBAAA;EnDw1JH;AmDn1JD;EACE,oBAAA;EACA,2BAAA;EACA,+BAAA;EvBpCA,iCAAA;EACC,gCAAA;E5B03JF;AmD70JD;EAEI,kBAAA;EnD80JH;AmDh1JD;EAKM,qBAAA;EACA,kBAAA;EnD80JL;AmD10JG;EAEI,eAAA;EvBlEN,8BAAA;EACC,6BAAA;E5B84JF;AmDx0JG;EAEI,kBAAA;EvBjEN,iCAAA;EACC,gCAAA;E5B24JF;AmDp0JD;EAEI,qBAAA;EnDq0JH;AmDl0JD;EACE,qBAAA;EnDo0JD;AmD5zJD;;;EAII,kBAAA;EnD6zJH;AmDj0JD;;EvB9FE,8BAAA;EACC,6BAAA;E5Bm6JF;AmDt0JD;;;;;;;;EAgBU,6BAAA;EnDg0JT;AmDh1JD;;;;;;;;EAoBU,8BAAA;EnDs0JT;AmD11JD;;EvBtFE,iCAAA;EACC,gCAAA;E5Bo7JF;AmD/1JD;;;;;;;;EAmCU,gCAAA;EnDs0JT;AmDz2JD;;;;;;;;EAuCU,iCAAA;EnD40JT;AmDn3JD;;EA8CI,+BAAA;EnDy0JH;AmDv3JD;;EAkDI,eAAA;EnDy0JH;AmD33JD;;EAsDI,WAAA;EnDy0JH;AmD/3JD;;;;;;;;;;;;EA6DU,gBAAA;EnDg1JT;AmD74JD;;;;;;;;;;;;EAiEU,iBAAA;EnD01JT;AmD35JD;;;;;;;;EA0EU,kBAAA;EnD21JT;AmDr6JD;;;;;;;;EAmFU,kBAAA;EnD41JT;AmD/6JD;EAyFI,WAAA;EACA,kBAAA;EnDy1JH;AmD/0JD;EACE,qBAAA;EnDi1JD;AmDl1JD;EAKI,kBAAA;EACA,oBAAA;EnDg1JH;AmDt1JD;EAQM,iBAAA;EnDi1JL;AmDz1JD;EAaI,kBAAA;EnD+0JH;AmD51JD;EAeM,+BAAA;EnDg1JL;AmD/1JD;EAmBI,eAAA;EnD+0JH;AmDl2JD;EAqBM,kCAAA;EnDg1JL;AmDz0JD;EC9NE,uBAAA;EpD0iKD;AoDxiKC;EACE,gBAAA;EACA,2BAAA;EACA,uBAAA;EpD0iKH;AoD7iKC;EAMI,2BAAA;EpD0iKL;AoDhjKC;EASI,gBAAA;EACA,2BAAA;EpD0iKL;AoDviKC;EAEI,8BAAA;EpDwiKL;AmDx1JD;ECjOE,uBAAA;EpD4jKD;AoD1jKC;EACE,gBAAA;EACA,2BAAA;EACA,uBAAA;EpD4jKH;AoD/jKC;EAMI,2BAAA;EpD4jKL;AoDlkKC;EASI,gBAAA;EACA,2BAAA;EpD4jKL;AoDzjKC;EAEI,8BAAA;EpD0jKL;AmDv2JD;ECpOE,uBAAA;EpD8kKD;AoD5kKC;EACE,gBAAA;EACA,2BAAA;EACA,uBAAA;EpD8kKH;AoDjlKC;EAMI,2BAAA;EpD8kKL;AoDplKC;EASI,gBAAA;EACA,2BAAA;EpD8kKL;AoD3kKC;EAEI,8BAAA;EpD4kKL;AmDt3JD;ECvOE,uBAAA;EpDgmKD;AoD9lKC;EACE,gBAAA;EACA,2BAAA;EACA,uBAAA;EpDgmKH;AoDnmKC;EAMI,2BAAA;EpDgmKL;AoDtmKC;EASI,gBAAA;EACA,2BAAA;EpDgmKL;AoD7lKC;EAEI,8BAAA;EpD8lKL;AmDr4JD;EC1OE,uBAAA;EpDknKD;AoDhnKC;EACE,gBAAA;EACA,2BAAA;EACA,uBAAA;EpDknKH;AoDrnKC;EAMI,2BAAA;EpDknKL;AoDxnKC;EASI,gBAAA;EACA,2BAAA;EpDknKL;AoD/mKC;EAEI,8BAAA;EpDgnKL;AmDp5JD;EC7OE,uBAAA;EpDooKD;AoDloKC;EACE,gBAAA;EACA,2BAAA;EACA,uBAAA;EpDooKH;AoDvoKC;EAMI,2BAAA;EpDooKL;AoD1oKC;EASI,gBAAA;EACA,2BAAA;EpDooKL;AoDjoKC;EAEI,8BAAA;EpDkoKL;AqDlpKD;EACE,oBAAA;EACA,gBAAA;EACA,WAAA;EACA,YAAA;EACA,kBAAA;ErDopKD;AqDzpKD;;;;EAWI,oBAAA;EACA,QAAA;EACA,SAAA;EACA,WAAA;EACA,cAAA;EACA,aAAA;EACA,WAAA;ErDopKH;AqDhpKC;EACE,wBAAA;ErDkpKH;AqD9oKC;EACE,qBAAA;ErDgpKH;AsDzqKD;EACE,kBAAA;EACA,eAAA;EACA,qBAAA;EACA,2BAAA;EACA,2BAAA;EACA,oBAAA;EjDwDA,yDAAA;EACQ,iDAAA;ELonKT;AsDnrKD;EASI,oBAAA;EACA,mCAAA;EtD6qKH;AsDxqKD;EACE,eAAA;EACA,oBAAA;EtD0qKD;AsDxqKD;EACE,cAAA;EACA,oBAAA;EtD0qKD;AuDhsKD;EACE,cAAA;EACA,iBAAA;EACA,mBAAA;EACA,gBAAA;EACA,gBAAA;EACA,8BAAA;EjCRA,cAAA;EAGA,2BAAA;EtBysKD;AuDjsKC;;EAEE,gBAAA;EACA,uBAAA;EACA,iBAAA;EjCfF,cAAA;EAGA,2BAAA;EtBitKD;AuD9rKC;EACE,YAAA;EACA,iBAAA;EACA,yBAAA;EACA,WAAA;EACA,0BAAA;EvDgsKH;AwDptKD;EACE,kBAAA;ExDstKD;AwDltKD;EACE,eAAA;EACA,kBAAA;EACA,iBAAA;EACA,QAAA;EACA,UAAA;EACA,WAAA;EACA,SAAA;EACA,eAAA;EACA,mCAAA;EAIA,YAAA;ExDitKD;AwD9sKC;EnDkHA,4CAAA;EACQ,uCAAA;EAAA,oCAAA;EA8DR,qDAAA;EAEK,2CAAA;EACG,qCAAA;ELkiKT;AwDltKC;EnD8GA,yCAAA;EACQ,oCAAA;EAAA,iCAAA;ELumKT;AwDptKD;EACE,oBAAA;EACA,kBAAA;ExDstKD;AwDltKD;EACE,oBAAA;EACA,aAAA;EACA,cAAA;ExDotKD;AwDhtKD;EACE,oBAAA;EACA,2BAAA;EACA,2BAAA;EACA,sCAAA;EACA,oBAAA;EnDaA,kDAAA;EACQ,0CAAA;EmDZR,sCAAA;EAAA,8BAAA;EAEA,YAAA;ExDktKD;AwD9sKD;EACE,iBAAA;EACA,QAAA;EACA,UAAA;EACA,WAAA;EACA,SAAA;EACA,eAAA;EACA,2BAAA;ExDgtKD;AwD9sKC;ElCrEA,YAAA;EAGA,0BAAA;EtBoxKD;AwDjtKC;ElCtEA,cAAA;EAGA,2BAAA;EtBwxKD;AwDhtKD;EACE,eAAA;EACA,kCAAA;EACA,2BAAA;ExDktKD;AwD/sKD;EACE,kBAAA;ExDitKD;AwD7sKD;EACE,WAAA;EACA,yBAAA;ExD+sKD;AwD1sKD;EACE,oBAAA;EACA,eAAA;ExD4sKD;AwDxsKD;EACE,eAAA;EACA,mBAAA;EACA,+BAAA;ExD0sKD;AwD7sKD;EAQI,kBAAA;EACA,kBAAA;ExDwsKH;AwDjtKD;EAaI,mBAAA;ExDusKH;AwDptKD;EAiBI,gBAAA;ExDssKH;AwDjsKD;EACE,oBAAA;EACA,cAAA;EACA,aAAA;EACA,cAAA;EACA,kBAAA;ExDmsKD;AwDjrKD;EAZE;IACE,cAAA;IACA,mBAAA;IxDgsKD;EwD9rKD;InDvEA,mDAAA;IACQ,2CAAA;ILwwKP;EwD7rKD;IAAY,cAAA;IxDgsKX;EACF;AwD3rKD;EAFE;IAAY,cAAA;IxDisKX;EACF;AyDh1KD;EACE,oBAAA;EACA,eAAA;EACA,gBAAA;EACA,qBAAA;EACA,iBAAA;EACA,kBAAA;EnCTA,YAAA;EAGA,0BAAA;EtB01KD;AyDj1KC;EnCZA,cAAA;EAGA,2BAAA;EtB81KD;AyDp1KC;EAAW,kBAAA;EAAmB,gBAAA;EzDw1K/B;AyDv1KC;EAAW,kBAAA;EAAmB,gBAAA;EzD21K/B;AyD11KC;EAAW,iBAAA;EAAmB,gBAAA;EzD81K/B;AyD71KC;EAAW,mBAAA;EAAmB,gBAAA;EzDi2K/B;AyD71KD;EACE,kBAAA;EACA,kBAAA;EACA,gBAAA;EACA,oBAAA;EACA,uBAAA;EACA,2BAAA;EACA,oBAAA;EzD+1KD;AyD31KD;EACE,oBAAA;EACA,UAAA;EACA,WAAA;EACA,2BAAA;EACA,qBAAA;EzD61KD;AyD11KC;EACE,WAAA;EACA,WAAA;EACA,mBAAA;EACA,yBAAA;EACA,2BAAA;EzD41KH;AyD11KC;EACE,WAAA;EACA,WAAA;EACA,yBAAA;EACA,2BAAA;EzD41KH;AyD11KC;EACE,WAAA;EACA,YAAA;EACA,yBAAA;EACA,2BAAA;EzD41KH;AyD11KC;EACE,UAAA;EACA,SAAA;EACA,kBAAA;EACA,6BAAA;EACA,6BAAA;EzD41KH;AyD11KC;EACE,UAAA;EACA,UAAA;EACA,kBAAA;EACA,6BAAA;EACA,4BAAA;EzD41KH;AyD11KC;EACE,QAAA;EACA,WAAA;EACA,mBAAA;EACA,yBAAA;EACA,8BAAA;EzD41KH;AyD11KC;EACE,QAAA;EACA,WAAA;EACA,yBAAA;EACA,8BAAA;EzD41KH;AyD11KC;EACE,QAAA;EACA,YAAA;EACA,yBAAA;EACA,8BAAA;EzD41KH;A0Dn7KD;EACE,oBAAA;EACA,QAAA;EACA,SAAA;EACA,eAAA;EACA,eAAA;EACA,kBAAA;EACA,cAAA;EACA,kBAAA;EACA,2BAAA;EACA,sCAAA;EAAA,8BAAA;EACA,2BAAA;EACA,sCAAA;EACA,oBAAA;ErDkDA,mDAAA;EACQ,2CAAA;EqD/CR,qBAAA;E1Do7KD;A0Dj7KC;EAAY,mBAAA;E1Do7Kb;A0Dn7KC;EAAY,mBAAA;E1Ds7Kb;A0Dr7KC;EAAY,kBAAA;E1Dw7Kb;A0Dv7KC;EAAY,oBAAA;E1D07Kb;A0Dv7KD;EACE,WAAA;EACA,mBAAA;EACA,iBAAA;EACA,qBAAA;EACA,mBAAA;EACA,2BAAA;EACA,kCAAA;EACA,4BAAA;E1Dy7KD;A0Dt7KD;EACE,mBAAA;E1Dw7KD;A0Dh7KC;;EAEE,oBAAA;EACA,gBAAA;EACA,UAAA;EACA,WAAA;EACA,2BAAA;EACA,qBAAA;E1Dk7KH;A0D/6KD;EACE,oBAAA;E1Di7KD;A0D/6KD;EACE,oBAAA;EACA,aAAA;E1Di7KD;A0D76KC;EACE,WAAA;EACA,oBAAA;EACA,wBAAA;EACA,2BAAA;EACA,uCAAA;EACA,eAAA;E1D+6KH;A0D96KG;EACE,cAAA;EACA,aAAA;EACA,oBAAA;EACA,wBAAA;EACA,2BAAA;E1Dg7KL;A0D76KC;EACE,UAAA;EACA,aAAA;EACA,mBAAA;EACA,sBAAA;EACA,6BAAA;EACA,yCAAA;E1D+6KH;A0D96KG;EACE,cAAA;EACA,WAAA;EACA,eAAA;EACA,sBAAA;EACA,6BAAA;E1Dg7KL;A0D76KC;EACE,WAAA;EACA,oBAAA;EACA,qBAAA;EACA,8BAAA;EACA,0CAAA;EACA,YAAA;E1D+6KH;A0D96KG;EACE,cAAA;EACA,UAAA;EACA,oBAAA;EACA,qBAAA;EACA,8BAAA;E1Dg7KL;A0D56KC;EACE,UAAA;EACA,cAAA;EACA,mBAAA;EACA,uBAAA;EACA,4BAAA;EACA,wCAAA;E1D86KH;A0D76KG;EACE,cAAA;EACA,YAAA;EACA,uBAAA;EACA,4BAAA;EACA,eAAA;E1D+6KL;A2DziLD;EACE,oBAAA;E3D2iLD;A2DxiLD;EACE,oBAAA;EACA,kBAAA;EACA,aAAA;E3D0iLD;A2D7iLD;EAMI,eAAA;EACA,oBAAA;EtD0KF,2CAAA;EACK,sCAAA;EACG,mCAAA;ELi4KT;A2DpjLD;;EAcM,gBAAA;E3D0iLL;A2DxjLD;;;EAqBI,gBAAA;E3DwiLH;A2D7jLD;EAyBI,SAAA;E3DuiLH;A2DhkLD;;EA8BI,oBAAA;EACA,QAAA;EACA,aAAA;E3DsiLH;A2DtkLD;EAoCI,YAAA;E3DqiLH;A2DzkLD;EAuCI,aAAA;E3DqiLH;A2D5kLD;;EA2CI,SAAA;E3DqiLH;A2DhlLD;EA+CI,aAAA;E3DoiLH;A2DnlLD;EAkDI,YAAA;E3DoiLH;A2D5hLD;EACE,oBAAA;EACA,QAAA;EACA,SAAA;EACA,WAAA;EACA,YAAA;ErCtEA,cAAA;EAGA,2BAAA;EqCqEA,iBAAA;EACA,gBAAA;EACA,oBAAA;EACA,2CAAA;E3D+hLD;A2D1hLC;Eb1EE,oGAAA;EACA,+FAAA;EACA,sHAAA;EAAA,gGAAA;EACA,6BAAA;EACA,wHAAA;E9CumLH;A2D9hLC;EACE,YAAA;EACA,UAAA;Eb/EA,oGAAA;EACA,+FAAA;EACA,sHAAA;EAAA,gGAAA;EACA,6BAAA;EACA,wHAAA;E9CgnLH;A2DhiLC;;EAEE,YAAA;EACA,gBAAA;EACA,uBAAA;ErC9FF,cAAA;EAGA,2BAAA;EtB+nLD;A2DjkLD;;;;EAsCI,oBAAA;EACA,UAAA;EACA,YAAA;EACA,uBAAA;E3DiiLH;A2D1kLD;;EA6CI,WAAA;EACA,oBAAA;E3DiiLH;A2D/kLD;;EAkDI,YAAA;EACA,qBAAA;E3DiiLH;A2DplLD;;EAuDI,aAAA;EACA,cAAA;EACA,mBAAA;EACA,oBAAA;E3DiiLH;A2D5hLG;EACE,kBAAA;E3D8hLL;A2D1hLG;EACE,kBAAA;E3D4hLL;A2DlhLD;EACE,oBAAA;EACA,cAAA;EACA,WAAA;EACA,aAAA;EACA,YAAA;EACA,mBAAA;EACA,iBAAA;EACA,kBAAA;EACA,oBAAA;E3DohLD;A2D7hLD;EAYI,uBAAA;EACA,aAAA;EACA,cAAA;EACA,aAAA;EACA,qBAAA;EACA,2BAAA;EACA,qBAAA;EACA,iBAAA;EAUA,2BAAA;EACA,oCAAA;E3D2gLH;A2DziLD;EAiCI,WAAA;EACA,aAAA;EACA,cAAA;EACA,2BAAA;E3D2gLH;A2DpgLD;EACE,oBAAA;EACA,WAAA;EACA,YAAA;EACA,cAAA;EACA,aAAA;EACA,mBAAA;EACA,sBAAA;EACA,gBAAA;EACA,oBAAA;EACA,2CAAA;E3DsgLD;A2DrgLC;EACE,mBAAA;E3DugLH;A2D99KD;EAhCE;;;;IAKI,aAAA;IACA,cAAA;IACA,mBAAA;IACA,iBAAA;I3DggLH;E2DxgLD;;IAYI,oBAAA;I3DggLH;E2D5gLD;;IAgBI,qBAAA;I3DggLH;E2D3/KD;IACE,WAAA;IACA,YAAA;IACA,sBAAA;I3D6/KD;E2Dz/KD;IACE,cAAA;I3D2/KD;EACF;A4D/tLC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAEE,cAAA;EACA,gBAAA;E5D6vLH;A4D3vLC;;;;;;;;;;;;;;;EACE,aAAA;E5D2wLH;AiCnxLD;E4BRE,gBAAA;EACA,mBAAA;EACA,oBAAA;E7D8xLD;AiCrxLD;EACE,yBAAA;EjCuxLD;AiCrxLD;EACE,wBAAA;EjCuxLD;AiC/wLD;EACE,0BAAA;EjCixLD;AiC/wLD;EACE,2BAAA;EjCixLD;AiC/wLD;EACE,oBAAA;EjCixLD;AiC/wLD;E6BzBE,aAAA;EACA,oBAAA;EACA,mBAAA;EACA,+BAAA;EACA,WAAA;E9D2yLD;AiC7wLD;EACE,0BAAA;EACA,+BAAA;EjC+wLD;AiCxwLD;EACE,iBAAA;E5B2FA,yCAAA;EACQ,oCAAA;EAAA,iCAAA;ELgrLT;A+D9yLD;EACE,qBAAA;E/DgzLD;A+D1yLD;;;;ECdE,0BAAA;EhE8zLD;A+DzyLD;;;;;;;;;;;;EAYE,0BAAA;E/D2yLD;A+DpyLD;EAAA;IChDE,2BAAA;IhEw1LC;EgEv1LD;IAAU,gBAAA;IhE01LT;EgEz1LD;IAAU,+BAAA;IhE41LT;EgE31LD;;IACU,gCAAA;IhE81LT;EACF;A+D9yLD;EAAA;IAFI,2BAAA;I/DozLD;EACF;A+D9yLD;EAAA;IAFI,4BAAA;I/DozLD;EACF;A+D9yLD;EAAA;IAFI,kCAAA;I/DozLD;EACF;A+D7yLD;EAAA;ICrEE,2BAAA;IhEs3LC;EgEr3LD;IAAU,gBAAA;IhEw3LT;EgEv3LD;IAAU,+BAAA;IhE03LT;EgEz3LD;;IACU,gCAAA;IhE43LT;EACF;A+DvzLD;EAAA;IAFI,2BAAA;I/D6zLD;EACF;A+DvzLD;EAAA;IAFI,4BAAA;I/D6zLD;EACF;A+DvzLD;EAAA;IAFI,kCAAA;I/D6zLD;EACF;A+DtzLD;EAAA;IC1FE,2BAAA;IhEo5LC;EgEn5LD;IAAU,gBAAA;IhEs5LT;EgEr5LD;IAAU,+BAAA;IhEw5LT;EgEv5LD;;IACU,gCAAA;IhE05LT;EACF;A+Dh0LD;EAAA;IAFI,2BAAA;I/Ds0LD;EACF;A+Dh0LD;EAAA;IAFI,4BAAA;I/Ds0LD;EACF;A+Dh0LD;EAAA;IAFI,kCAAA;I/Ds0LD;EACF;A+D/zLD;EAAA;IC/GE,2BAAA;IhEk7LC;EgEj7LD;IAAU,gBAAA;IhEo7LT;EgEn7LD;IAAU,+BAAA;IhEs7LT;EgEr7LD;;IACU,gCAAA;IhEw7LT;EACF;A+Dz0LD;EAAA;IAFI,2BAAA;I/D+0LD;EACF;A+Dz0LD;EAAA;IAFI,4BAAA;I/D+0LD;EACF;A+Dz0LD;EAAA;IAFI,kCAAA;I/D+0LD;EACF;A+Dx0LD;EAAA;IC5HE,0BAAA;IhEw8LC;EACF;A+Dx0LD;EAAA;ICjIE,0BAAA;IhE68LC;EACF;A+Dx0LD;EAAA;ICtIE,0BAAA;IhEk9LC;EACF;A+Dx0LD;EAAA;IC3IE,0BAAA;IhEu9LC;EACF;A+Dr0LD;ECnJE,0BAAA;EhE29LD;A+Dl0LD;EAAA;ICjKE,2BAAA;IhEu+LC;EgEt+LD;IAAU,gBAAA;IhEy+LT;EgEx+LD;IAAU,+BAAA;IhE2+LT;EgE1+LD;;IACU,gCAAA;IhE6+LT;EACF;A+Dh1LD;EACE,0BAAA;E/Dk1LD;A+D70LD;EAAA;IAFI,2BAAA;I/Dm1LD;EACF;A+Dj1LD;EACE,0BAAA;E/Dm1LD;A+D90LD;EAAA;IAFI,4BAAA;I/Do1LD;EACF;A+Dl1LD;EACE,0BAAA;E/Do1LD;A+D/0LD;EAAA;IAFI,kCAAA;I/Dq1LD;EACF;A+D90LD;EAAA;ICpLE,0BAAA;IhEsgMC;EACF","sourcesContent":[null,"/*! normalize.css v3.0.1 | MIT License | git.io/normalize */\n\n//\n// 1. Set default font family to sans-serif.\n// 2. Prevent iOS text size adjust after orientation change, without disabling\n// user zoom.\n//\n\nhtml {\n font-family: sans-serif; // 1\n -ms-text-size-adjust: 100%; // 2\n -webkit-text-size-adjust: 100%; // 2\n}\n\n//\n// Remove default margin.\n//\n\nbody {\n margin: 0;\n}\n\n// HTML5 display definitions\n// ==========================================================================\n\n//\n// Correct `block` display not defined for any HTML5 element in IE 8/9.\n// Correct `block` display not defined for `details` or `summary` in IE 10/11 and Firefox.\n// Correct `block` display not defined for `main` in IE 11.\n//\n\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nnav,\nsection,\nsummary {\n display: block;\n}\n\n//\n// 1. Correct `inline-block` display not defined in IE 8/9.\n// 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.\n//\n\naudio,\ncanvas,\nprogress,\nvideo {\n display: inline-block; // 1\n vertical-align: baseline; // 2\n}\n\n//\n// Prevent modern browsers from displaying `audio` without controls.\n// Remove excess height in iOS 5 devices.\n//\n\naudio:not([controls]) {\n display: none;\n height: 0;\n}\n\n//\n// Address `[hidden]` styling not present in IE 8/9/10.\n// Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22.\n//\n\n[hidden],\ntemplate {\n display: none;\n}\n\n// Links\n// ==========================================================================\n\n//\n// Remove the gray background color from active links in IE 10.\n//\n\na {\n background: transparent;\n}\n\n//\n// Improve readability when focused and also mouse hovered in all browsers.\n//\n\na:active,\na:hover {\n outline: 0;\n}\n\n// Text-level semantics\n// ==========================================================================\n\n//\n// Address styling not present in IE 8/9/10/11, Safari, and Chrome.\n//\n\nabbr[title] {\n border-bottom: 1px dotted;\n}\n\n//\n// Address style set to `bolder` in Firefox 4+, Safari, and Chrome.\n//\n\nb,\nstrong {\n font-weight: bold;\n}\n\n//\n// Address styling not present in Safari and Chrome.\n//\n\ndfn {\n font-style: italic;\n}\n\n//\n// Address variable `h1` font-size and margin within `section` and `article`\n// contexts in Firefox 4+, Safari, and Chrome.\n//\n\nh1 {\n font-size: 2em;\n margin: 0.67em 0;\n}\n\n//\n// Address styling not present in IE 8/9.\n//\n\nmark {\n background: #ff0;\n color: #000;\n}\n\n//\n// Address inconsistent and variable font size in all browsers.\n//\n\nsmall {\n font-size: 80%;\n}\n\n//\n// Prevent `sub` and `sup` affecting `line-height` in all browsers.\n//\n\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\n\nsup {\n top: -0.5em;\n}\n\nsub {\n bottom: -0.25em;\n}\n\n// Embedded content\n// ==========================================================================\n\n//\n// Remove border when inside `a` element in IE 8/9/10.\n//\n\nimg {\n border: 0;\n}\n\n//\n// Correct overflow not hidden in IE 9/10/11.\n//\n\nsvg:not(:root) {\n overflow: hidden;\n}\n\n// Grouping content\n// ==========================================================================\n\n//\n// Address margin not present in IE 8/9 and Safari.\n//\n\nfigure {\n margin: 1em 40px;\n}\n\n//\n// Address differences between Firefox and other browsers.\n//\n\nhr {\n -moz-box-sizing: content-box;\n box-sizing: content-box;\n height: 0;\n}\n\n//\n// Contain overflow in all browsers.\n//\n\npre {\n overflow: auto;\n}\n\n//\n// Address odd `em`-unit font size rendering in all browsers.\n//\n\ncode,\nkbd,\npre,\nsamp {\n font-family: monospace, monospace;\n font-size: 1em;\n}\n\n// Forms\n// ==========================================================================\n\n//\n// Known limitation: by default, Chrome and Safari on OS X allow very limited\n// styling of `select`, unless a `border` property is set.\n//\n\n//\n// 1. Correct color not being inherited.\n// Known issue: affects color of disabled elements.\n// 2. Correct font properties not being inherited.\n// 3. Address margins set differently in Firefox 4+, Safari, and Chrome.\n//\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n color: inherit; // 1\n font: inherit; // 2\n margin: 0; // 3\n}\n\n//\n// Address `overflow` set to `hidden` in IE 8/9/10/11.\n//\n\nbutton {\n overflow: visible;\n}\n\n//\n// Address inconsistent `text-transform` inheritance for `button` and `select`.\n// All other form control elements do not inherit `text-transform` values.\n// Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.\n// Correct `select` style inheritance in Firefox.\n//\n\nbutton,\nselect {\n text-transform: none;\n}\n\n//\n// 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`\n// and `video` controls.\n// 2. Correct inability to style clickable `input` types in iOS.\n// 3. Improve usability and consistency of cursor style between image-type\n// `input` and others.\n//\n\nbutton,\nhtml input[type=\"button\"], // 1\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n -webkit-appearance: button; // 2\n cursor: pointer; // 3\n}\n\n//\n// Re-set default cursor for disabled elements.\n//\n\nbutton[disabled],\nhtml input[disabled] {\n cursor: default;\n}\n\n//\n// Remove inner padding and border in Firefox 4+.\n//\n\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n border: 0;\n padding: 0;\n}\n\n//\n// Address Firefox 4+ setting `line-height` on `input` using `!important` in\n// the UA stylesheet.\n//\n\ninput {\n line-height: normal;\n}\n\n//\n// It's recommended that you don't attempt to style these elements.\n// Firefox's implementation doesn't respect box-sizing, padding, or width.\n//\n// 1. Address box sizing set to `content-box` in IE 8/9/10.\n// 2. Remove excess padding in IE 8/9/10.\n//\n\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n box-sizing: border-box; // 1\n padding: 0; // 2\n}\n\n//\n// Fix the cursor style for Chrome's increment/decrement buttons. For certain\n// `font-size` values of the `input`, it causes the cursor style of the\n// decrement button to change from `default` to `text`.\n//\n\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n//\n// 1. Address `appearance` set to `searchfield` in Safari and Chrome.\n// 2. Address `box-sizing` set to `border-box` in Safari and Chrome\n// (include `-moz` to future-proof).\n//\n\ninput[type=\"search\"] {\n -webkit-appearance: textfield; // 1\n -moz-box-sizing: content-box;\n -webkit-box-sizing: content-box; // 2\n box-sizing: content-box;\n}\n\n//\n// Remove inner padding and search cancel button in Safari and Chrome on OS X.\n// Safari (but not Chrome) clips the cancel button when the search input has\n// padding (and `textfield` appearance).\n//\n\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n//\n// Define consistent border, margin, and padding.\n//\n\nfieldset {\n border: 1px solid #c0c0c0;\n margin: 0 2px;\n padding: 0.35em 0.625em 0.75em;\n}\n\n//\n// 1. Correct `color` not being inherited in IE 8/9/10/11.\n// 2. Remove padding so people aren't caught out if they zero out fieldsets.\n//\n\nlegend {\n border: 0; // 1\n padding: 0; // 2\n}\n\n//\n// Remove default vertical scrollbar in IE 8/9/10/11.\n//\n\ntextarea {\n overflow: auto;\n}\n\n//\n// Don't inherit the `font-weight` (applied by a rule above).\n// NOTE: the default cannot safely be changed in Chrome and Safari on OS X.\n//\n\noptgroup {\n font-weight: bold;\n}\n\n// Tables\n// ==========================================================================\n\n//\n// Remove most spacing between table cells.\n//\n\ntable {\n border-collapse: collapse;\n border-spacing: 0;\n}\n\ntd,\nth {\n padding: 0;\n}\n","//\n// Basic print styles\n// --------------------------------------------------\n// Source: https://github.com/h5bp/html5-boilerplate/blob/master/css/main.css\n\n@media print {\n\n * {\n text-shadow: none !important;\n color: #000 !important; // Black prints faster: h5bp.com/s\n background: transparent !important;\n box-shadow: none !important;\n }\n\n a,\n a:visited {\n text-decoration: underline;\n }\n\n a[href]:after {\n content: \" (\" attr(href) \")\";\n }\n\n abbr[title]:after {\n content: \" (\" attr(title) \")\";\n }\n\n // Don't show links for images, or javascript/internal links\n a[href^=\"javascript:\"]:after,\n a[href^=\"#\"]:after {\n content: \"\";\n }\n\n pre,\n blockquote {\n border: 1px solid #999;\n page-break-inside: avoid;\n }\n\n thead {\n display: table-header-group; // h5bp.com/t\n }\n\n tr,\n img {\n page-break-inside: avoid;\n }\n\n img {\n max-width: 100% !important;\n }\n\n p,\n h2,\n h3 {\n orphans: 3;\n widows: 3;\n }\n\n h2,\n h3 {\n page-break-after: avoid;\n }\n\n // Chrome (OSX) fix for https://github.com/twbs/bootstrap/issues/11245\n // Once fixed, we can just straight up remove this.\n select {\n background: #fff !important;\n }\n\n // Bootstrap components\n .navbar {\n display: none;\n }\n .table {\n td,\n th {\n background-color: #fff !important;\n }\n }\n .btn,\n .dropup > .btn {\n > .caret {\n border-top-color: #000 !important;\n }\n }\n .label {\n border: 1px solid #000;\n }\n\n .table {\n border-collapse: collapse !important;\n }\n .table-bordered {\n th,\n td {\n border: 1px solid #ddd !important;\n }\n }\n\n}\n","//\n// Glyphicons for Bootstrap\n//\n// Since icons are fonts, they can be placed anywhere text is placed and are\n// thus automatically sized to match the surrounding child. To use, create an\n// inline element with the appropriate classes, like so:\n//\n// Star\n\n// Import the fonts\n@font-face {\n font-family: 'Glyphicons Halflings';\n src: url('@{icon-font-path}@{icon-font-name}.eot');\n src: url('@{icon-font-path}@{icon-font-name}.eot?#iefix') format('embedded-opentype'),\n url('@{icon-font-path}@{icon-font-name}.woff') format('woff'),\n url('@{icon-font-path}@{icon-font-name}.ttf') format('truetype'),\n url('@{icon-font-path}@{icon-font-name}.svg#@{icon-font-svg-id}') format('svg');\n}\n\n// Catchall baseclass\n.glyphicon {\n position: relative;\n top: 1px;\n display: inline-block;\n font-family: 'Glyphicons Halflings';\n font-style: normal;\n font-weight: normal;\n line-height: 1;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n// Individual icons\n.glyphicon-asterisk { &:before { content: \"\\2a\"; } }\n.glyphicon-plus { &:before { content: \"\\2b\"; } }\n.glyphicon-euro { &:before { content: \"\\20ac\"; } }\n.glyphicon-minus { &:before { content: \"\\2212\"; } }\n.glyphicon-cloud { &:before { content: \"\\2601\"; } }\n.glyphicon-envelope { &:before { content: \"\\2709\"; } }\n.glyphicon-pencil { &:before { content: \"\\270f\"; } }\n.glyphicon-glass { &:before { content: \"\\e001\"; } }\n.glyphicon-music { &:before { content: \"\\e002\"; } }\n.glyphicon-search { &:before { content: \"\\e003\"; } }\n.glyphicon-heart { &:before { content: \"\\e005\"; } }\n.glyphicon-star { &:before { content: \"\\e006\"; } }\n.glyphicon-star-empty { &:before { content: \"\\e007\"; } }\n.glyphicon-user { &:before { content: \"\\e008\"; } }\n.glyphicon-film { &:before { content: \"\\e009\"; } }\n.glyphicon-th-large { &:before { content: \"\\e010\"; } }\n.glyphicon-th { &:before { content: \"\\e011\"; } }\n.glyphicon-th-list { &:before { content: \"\\e012\"; } }\n.glyphicon-ok { &:before { content: \"\\e013\"; } }\n.glyphicon-remove { &:before { content: \"\\e014\"; } }\n.glyphicon-zoom-in { &:before { content: \"\\e015\"; } }\n.glyphicon-zoom-out { &:before { content: \"\\e016\"; } }\n.glyphicon-off { &:before { content: \"\\e017\"; } }\n.glyphicon-signal { &:before { content: \"\\e018\"; } }\n.glyphicon-cog { &:before { content: \"\\e019\"; } }\n.glyphicon-trash { &:before { content: \"\\e020\"; } }\n.glyphicon-home { &:before { content: \"\\e021\"; } }\n.glyphicon-file { &:before { content: \"\\e022\"; } }\n.glyphicon-time { &:before { content: \"\\e023\"; } }\n.glyphicon-road { &:before { content: \"\\e024\"; } }\n.glyphicon-download-alt { &:before { content: \"\\e025\"; } }\n.glyphicon-download { &:before { content: \"\\e026\"; } }\n.glyphicon-upload { &:before { content: \"\\e027\"; } }\n.glyphicon-inbox { &:before { content: \"\\e028\"; } }\n.glyphicon-play-circle { &:before { content: \"\\e029\"; } }\n.glyphicon-repeat { &:before { content: \"\\e030\"; } }\n.glyphicon-refresh { &:before { content: \"\\e031\"; } }\n.glyphicon-list-alt { &:before { content: \"\\e032\"; } }\n.glyphicon-lock { &:before { content: \"\\e033\"; } }\n.glyphicon-flag { &:before { content: \"\\e034\"; } }\n.glyphicon-headphones { &:before { content: \"\\e035\"; } }\n.glyphicon-volume-off { &:before { content: \"\\e036\"; } }\n.glyphicon-volume-down { &:before { content: \"\\e037\"; } }\n.glyphicon-volume-up { &:before { content: \"\\e038\"; } }\n.glyphicon-qrcode { &:before { content: \"\\e039\"; } }\n.glyphicon-barcode { &:before { content: \"\\e040\"; } }\n.glyphicon-tag { &:before { content: \"\\e041\"; } }\n.glyphicon-tags { &:before { content: \"\\e042\"; } }\n.glyphicon-book { &:before { content: \"\\e043\"; } }\n.glyphicon-bookmark { &:before { content: \"\\e044\"; } }\n.glyphicon-print { &:before { content: \"\\e045\"; } }\n.glyphicon-camera { &:before { content: \"\\e046\"; } }\n.glyphicon-font { &:before { content: \"\\e047\"; } }\n.glyphicon-bold { &:before { content: \"\\e048\"; } }\n.glyphicon-italic { &:before { content: \"\\e049\"; } }\n.glyphicon-text-height { &:before { content: \"\\e050\"; } }\n.glyphicon-text-width { &:before { content: \"\\e051\"; } }\n.glyphicon-align-left { &:before { content: \"\\e052\"; } }\n.glyphicon-align-center { &:before { content: \"\\e053\"; } }\n.glyphicon-align-right { &:before { content: \"\\e054\"; } }\n.glyphicon-align-justify { &:before { content: \"\\e055\"; } }\n.glyphicon-list { &:before { content: \"\\e056\"; } }\n.glyphicon-indent-left { &:before { content: \"\\e057\"; } }\n.glyphicon-indent-right { &:before { content: \"\\e058\"; } }\n.glyphicon-facetime-video { &:before { content: \"\\e059\"; } }\n.glyphicon-picture { &:before { content: \"\\e060\"; } }\n.glyphicon-map-marker { &:before { content: \"\\e062\"; } }\n.glyphicon-adjust { &:before { content: \"\\e063\"; } }\n.glyphicon-tint { &:before { content: \"\\e064\"; } }\n.glyphicon-edit { &:before { content: \"\\e065\"; } }\n.glyphicon-share { &:before { content: \"\\e066\"; } }\n.glyphicon-check { &:before { content: \"\\e067\"; } }\n.glyphicon-move { &:before { content: \"\\e068\"; } }\n.glyphicon-step-backward { &:before { content: \"\\e069\"; } }\n.glyphicon-fast-backward { &:before { content: \"\\e070\"; } }\n.glyphicon-backward { &:before { content: \"\\e071\"; } }\n.glyphicon-play { &:before { content: \"\\e072\"; } }\n.glyphicon-pause { &:before { content: \"\\e073\"; } }\n.glyphicon-stop { &:before { content: \"\\e074\"; } }\n.glyphicon-forward { &:before { content: \"\\e075\"; } }\n.glyphicon-fast-forward { &:before { content: \"\\e076\"; } }\n.glyphicon-step-forward { &:before { content: \"\\e077\"; } }\n.glyphicon-eject { &:before { content: \"\\e078\"; } }\n.glyphicon-chevron-left { &:before { content: \"\\e079\"; } }\n.glyphicon-chevron-right { &:before { content: \"\\e080\"; } }\n.glyphicon-plus-sign { &:before { content: \"\\e081\"; } }\n.glyphicon-minus-sign { &:before { content: \"\\e082\"; } }\n.glyphicon-remove-sign { &:before { content: \"\\e083\"; } }\n.glyphicon-ok-sign { &:before { content: \"\\e084\"; } }\n.glyphicon-question-sign { &:before { content: \"\\e085\"; } }\n.glyphicon-info-sign { &:before { content: \"\\e086\"; } }\n.glyphicon-screenshot { &:before { content: \"\\e087\"; } }\n.glyphicon-remove-circle { &:before { content: \"\\e088\"; } }\n.glyphicon-ok-circle { &:before { content: \"\\e089\"; } }\n.glyphicon-ban-circle { &:before { content: \"\\e090\"; } }\n.glyphicon-arrow-left { &:before { content: \"\\e091\"; } }\n.glyphicon-arrow-right { &:before { content: \"\\e092\"; } }\n.glyphicon-arrow-up { &:before { content: \"\\e093\"; } }\n.glyphicon-arrow-down { &:before { content: \"\\e094\"; } }\n.glyphicon-share-alt { &:before { content: \"\\e095\"; } }\n.glyphicon-resize-full { &:before { content: \"\\e096\"; } }\n.glyphicon-resize-small { &:before { content: \"\\e097\"; } }\n.glyphicon-exclamation-sign { &:before { content: \"\\e101\"; } }\n.glyphicon-gift { &:before { content: \"\\e102\"; } }\n.glyphicon-leaf { &:before { content: \"\\e103\"; } }\n.glyphicon-fire { &:before { content: \"\\e104\"; } }\n.glyphicon-eye-open { &:before { content: \"\\e105\"; } }\n.glyphicon-eye-close { &:before { content: \"\\e106\"; } }\n.glyphicon-warning-sign { &:before { content: \"\\e107\"; } }\n.glyphicon-plane { &:before { content: \"\\e108\"; } }\n.glyphicon-calendar { &:before { content: \"\\e109\"; } }\n.glyphicon-random { &:before { content: \"\\e110\"; } }\n.glyphicon-comment { &:before { content: \"\\e111\"; } }\n.glyphicon-magnet { &:before { content: \"\\e112\"; } }\n.glyphicon-chevron-up { &:before { content: \"\\e113\"; } }\n.glyphicon-chevron-down { &:before { content: \"\\e114\"; } }\n.glyphicon-retweet { &:before { content: \"\\e115\"; } }\n.glyphicon-shopping-cart { &:before { content: \"\\e116\"; } }\n.glyphicon-folder-close { &:before { content: \"\\e117\"; } }\n.glyphicon-folder-open { &:before { content: \"\\e118\"; } }\n.glyphicon-resize-vertical { &:before { content: \"\\e119\"; } }\n.glyphicon-resize-horizontal { &:before { content: \"\\e120\"; } }\n.glyphicon-hdd { &:before { content: \"\\e121\"; } }\n.glyphicon-bullhorn { &:before { content: \"\\e122\"; } }\n.glyphicon-bell { &:before { content: \"\\e123\"; } }\n.glyphicon-certificate { &:before { content: \"\\e124\"; } }\n.glyphicon-thumbs-up { &:before { content: \"\\e125\"; } }\n.glyphicon-thumbs-down { &:before { content: \"\\e126\"; } }\n.glyphicon-hand-right { &:before { content: \"\\e127\"; } }\n.glyphicon-hand-left { &:before { content: \"\\e128\"; } }\n.glyphicon-hand-up { &:before { content: \"\\e129\"; } }\n.glyphicon-hand-down { &:before { content: \"\\e130\"; } }\n.glyphicon-circle-arrow-right { &:before { content: \"\\e131\"; } }\n.glyphicon-circle-arrow-left { &:before { content: \"\\e132\"; } }\n.glyphicon-circle-arrow-up { &:before { content: \"\\e133\"; } }\n.glyphicon-circle-arrow-down { &:before { content: \"\\e134\"; } }\n.glyphicon-globe { &:before { content: \"\\e135\"; } }\n.glyphicon-wrench { &:before { content: \"\\e136\"; } }\n.glyphicon-tasks { &:before { content: \"\\e137\"; } }\n.glyphicon-filter { &:before { content: \"\\e138\"; } }\n.glyphicon-briefcase { &:before { content: \"\\e139\"; } }\n.glyphicon-fullscreen { &:before { content: \"\\e140\"; } }\n.glyphicon-dashboard { &:before { content: \"\\e141\"; } }\n.glyphicon-paperclip { &:before { content: \"\\e142\"; } }\n.glyphicon-heart-empty { &:before { content: \"\\e143\"; } }\n.glyphicon-link { &:before { content: \"\\e144\"; } }\n.glyphicon-phone { &:before { content: \"\\e145\"; } }\n.glyphicon-pushpin { &:before { content: \"\\e146\"; } }\n.glyphicon-usd { &:before { content: \"\\e148\"; } }\n.glyphicon-gbp { &:before { content: \"\\e149\"; } }\n.glyphicon-sort { &:before { content: \"\\e150\"; } }\n.glyphicon-sort-by-alphabet { &:before { content: \"\\e151\"; } }\n.glyphicon-sort-by-alphabet-alt { &:before { content: \"\\e152\"; } }\n.glyphicon-sort-by-order { &:before { content: \"\\e153\"; } }\n.glyphicon-sort-by-order-alt { &:before { content: \"\\e154\"; } }\n.glyphicon-sort-by-attributes { &:before { content: \"\\e155\"; } }\n.glyphicon-sort-by-attributes-alt { &:before { content: \"\\e156\"; } }\n.glyphicon-unchecked { &:before { content: \"\\e157\"; } }\n.glyphicon-expand { &:before { content: \"\\e158\"; } }\n.glyphicon-collapse-down { &:before { content: \"\\e159\"; } }\n.glyphicon-collapse-up { &:before { content: \"\\e160\"; } }\n.glyphicon-log-in { &:before { content: \"\\e161\"; } }\n.glyphicon-flash { &:before { content: \"\\e162\"; } }\n.glyphicon-log-out { &:before { content: \"\\e163\"; } }\n.glyphicon-new-window { &:before { content: \"\\e164\"; } }\n.glyphicon-record { &:before { content: \"\\e165\"; } }\n.glyphicon-save { &:before { content: \"\\e166\"; } }\n.glyphicon-open { &:before { content: \"\\e167\"; } }\n.glyphicon-saved { &:before { content: \"\\e168\"; } }\n.glyphicon-import { &:before { content: \"\\e169\"; } }\n.glyphicon-export { &:before { content: \"\\e170\"; } }\n.glyphicon-send { &:before { content: \"\\e171\"; } }\n.glyphicon-floppy-disk { &:before { content: \"\\e172\"; } }\n.glyphicon-floppy-saved { &:before { content: \"\\e173\"; } }\n.glyphicon-floppy-remove { &:before { content: \"\\e174\"; } }\n.glyphicon-floppy-save { &:before { content: \"\\e175\"; } }\n.glyphicon-floppy-open { &:before { content: \"\\e176\"; } }\n.glyphicon-credit-card { &:before { content: \"\\e177\"; } }\n.glyphicon-transfer { &:before { content: \"\\e178\"; } }\n.glyphicon-cutlery { &:before { content: \"\\e179\"; } }\n.glyphicon-header { &:before { content: \"\\e180\"; } }\n.glyphicon-compressed { &:before { content: \"\\e181\"; } }\n.glyphicon-earphone { &:before { content: \"\\e182\"; } }\n.glyphicon-phone-alt { &:before { content: \"\\e183\"; } }\n.glyphicon-tower { &:before { content: \"\\e184\"; } }\n.glyphicon-stats { &:before { content: \"\\e185\"; } }\n.glyphicon-sd-video { &:before { content: \"\\e186\"; } }\n.glyphicon-hd-video { &:before { content: \"\\e187\"; } }\n.glyphicon-subtitles { &:before { content: \"\\e188\"; } }\n.glyphicon-sound-stereo { &:before { content: \"\\e189\"; } }\n.glyphicon-sound-dolby { &:before { content: \"\\e190\"; } }\n.glyphicon-sound-5-1 { &:before { content: \"\\e191\"; } }\n.glyphicon-sound-6-1 { &:before { content: \"\\e192\"; } }\n.glyphicon-sound-7-1 { &:before { content: \"\\e193\"; } }\n.glyphicon-copyright-mark { &:before { content: \"\\e194\"; } }\n.glyphicon-registration-mark { &:before { content: \"\\e195\"; } }\n.glyphicon-cloud-download { &:before { content: \"\\e197\"; } }\n.glyphicon-cloud-upload { &:before { content: \"\\e198\"; } }\n.glyphicon-tree-conifer { &:before { content: \"\\e199\"; } }\n.glyphicon-tree-deciduous { &:before { content: \"\\e200\"; } }\n","//\n// Scaffolding\n// --------------------------------------------------\n\n\n// Reset the box-sizing\n//\n// Heads up! This reset may cause conflicts with some third-party widgets.\n// For recommendations on resolving such conflicts, see\n// http://getbootstrap.com/getting-started/#third-box-sizing\n* {\n .box-sizing(border-box);\n}\n*:before,\n*:after {\n .box-sizing(border-box);\n}\n\n\n// Body reset\n\nhtml {\n font-size: 10px;\n -webkit-tap-highlight-color: rgba(0,0,0,0);\n}\n\nbody {\n font-family: @font-family-base;\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @text-color;\n background-color: @body-bg;\n}\n\n// Reset fonts for relevant elements\ninput,\nbutton,\nselect,\ntextarea {\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\n\n// Links\n\na {\n color: @link-color;\n text-decoration: none;\n\n &:hover,\n &:focus {\n color: @link-hover-color;\n text-decoration: underline;\n }\n\n &:focus {\n .tab-focus();\n }\n}\n\n\n// Figures\n//\n// We reset this here because previously Normalize had no `figure` margins. This\n// ensures we don't break anyone's use of the element.\n\nfigure {\n margin: 0;\n}\n\n\n// Images\n\nimg {\n vertical-align: middle;\n}\n\n// Responsive images (ensure images don't scale beyond their parents)\n.img-responsive {\n .img-responsive();\n}\n\n// Rounded corners\n.img-rounded {\n border-radius: @border-radius-large;\n}\n\n// Image thumbnails\n//\n// Heads up! This is mixin-ed into thumbnails.less for `.thumbnail`.\n.img-thumbnail {\n padding: @thumbnail-padding;\n line-height: @line-height-base;\n background-color: @thumbnail-bg;\n border: 1px solid @thumbnail-border;\n border-radius: @thumbnail-border-radius;\n .transition(all .2s ease-in-out);\n\n // Keep them at most 100% wide\n .img-responsive(inline-block);\n}\n\n// Perfect circle\n.img-circle {\n border-radius: 50%; // set radius in percents\n}\n\n\n// Horizontal rules\n\nhr {\n margin-top: @line-height-computed;\n margin-bottom: @line-height-computed;\n border: 0;\n border-top: 1px solid @hr-border;\n}\n\n\n// Only display content to screen readers\n//\n// See: http://a11yproject.com/posts/how-to-hide-content/\n\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0,0,0,0);\n border: 0;\n}\n\n// Use in conjunction with .sr-only to only display content when it's focused.\n// Useful for \"Skip to main content\" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1\n// Credit: HTML5 Boilerplate\n\n.sr-only-focusable {\n &:active,\n &:focus {\n position: static;\n width: auto;\n height: auto;\n margin: 0;\n overflow: visible;\n clip: auto;\n }\n}\n","// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They will be removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility){\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n word-wrap: break-word;\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n &::-moz-placeholder { color: @color; // Firefox\n opacity: 1; } // See https://github.com/twbs/bootstrap/pull/11526\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n","// WebKit-style focus\n\n.tab-focus() {\n // Default\n outline: thin dotted;\n // WebKit\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\n","// Image Mixins\n// - Responsive image\n// - Retina image\n\n\n// Responsive image\n//\n// Keep images from scaling beyond the width of their parents.\n.img-responsive(@display: block) {\n display: @display;\n width: 100% \\9; // Force IE10 and below to size SVG images correctly\n max-width: 100%; // Part 1: Set a maximum relative to the parent\n height: auto; // Part 2: Scale the height according to the width, otherwise you get stretching\n}\n\n\n// Retina image\n//\n// Short retina mixin for setting background-image and -size. Note that the\n// spelling of `min--moz-device-pixel-ratio` is intentional.\n.img-retina(@file-1x; @file-2x; @width-1x; @height-1x) {\n background-image: url(\"@{file-1x}\");\n\n @media\n only screen and (-webkit-min-device-pixel-ratio: 2),\n only screen and ( min--moz-device-pixel-ratio: 2),\n only screen and ( -o-min-device-pixel-ratio: 2/1),\n only screen and ( min-device-pixel-ratio: 2),\n only screen and ( min-resolution: 192dpi),\n only screen and ( min-resolution: 2dppx) {\n background-image: url(\"@{file-2x}\");\n background-size: @width-1x @height-1x;\n }\n}\n","//\n// Typography\n// --------------------------------------------------\n\n\n// Headings\n// -------------------------\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n font-family: @headings-font-family;\n font-weight: @headings-font-weight;\n line-height: @headings-line-height;\n color: @headings-color;\n\n small,\n .small {\n font-weight: normal;\n line-height: 1;\n color: @headings-small-color;\n }\n}\n\nh1, .h1,\nh2, .h2,\nh3, .h3 {\n margin-top: @line-height-computed;\n margin-bottom: (@line-height-computed / 2);\n\n small,\n .small {\n font-size: 65%;\n }\n}\nh4, .h4,\nh5, .h5,\nh6, .h6 {\n margin-top: (@line-height-computed / 2);\n margin-bottom: (@line-height-computed / 2);\n\n small,\n .small {\n font-size: 75%;\n }\n}\n\nh1, .h1 { font-size: @font-size-h1; }\nh2, .h2 { font-size: @font-size-h2; }\nh3, .h3 { font-size: @font-size-h3; }\nh4, .h4 { font-size: @font-size-h4; }\nh5, .h5 { font-size: @font-size-h5; }\nh6, .h6 { font-size: @font-size-h6; }\n\n\n// Body text\n// -------------------------\n\np {\n margin: 0 0 (@line-height-computed / 2);\n}\n\n.lead {\n margin-bottom: @line-height-computed;\n font-size: floor((@font-size-base * 1.15));\n font-weight: 300;\n line-height: 1.4;\n\n @media (min-width: @screen-sm-min) {\n font-size: (@font-size-base * 1.5);\n }\n}\n\n\n// Emphasis & misc\n// -------------------------\n\n// Ex: (12px small font / 14px base font) * 100% = about 85%\nsmall,\n.small {\n font-size: floor((100% * @font-size-small / @font-size-base));\n}\n\n// Undo browser default styling\ncite {\n font-style: normal;\n}\n\nmark,\n.mark {\n background-color: @state-warning-bg;\n padding: .2em;\n}\n\n// Alignment\n.text-left { text-align: left; }\n.text-right { text-align: right; }\n.text-center { text-align: center; }\n.text-justify { text-align: justify; }\n.text-nowrap { white-space: nowrap; }\n\n// Transformation\n.text-lowercase { text-transform: lowercase; }\n.text-uppercase { text-transform: uppercase; }\n.text-capitalize { text-transform: capitalize; }\n\n// Contextual colors\n.text-muted {\n color: @text-muted;\n}\n.text-primary {\n .text-emphasis-variant(@brand-primary);\n}\n.text-success {\n .text-emphasis-variant(@state-success-text);\n}\n.text-info {\n .text-emphasis-variant(@state-info-text);\n}\n.text-warning {\n .text-emphasis-variant(@state-warning-text);\n}\n.text-danger {\n .text-emphasis-variant(@state-danger-text);\n}\n\n// Contextual backgrounds\n// For now we'll leave these alongside the text classes until v4 when we can\n// safely shift things around (per SemVer rules).\n.bg-primary {\n // Given the contrast here, this is the only class to have its color inverted\n // automatically.\n color: #fff;\n .bg-variant(@brand-primary);\n}\n.bg-success {\n .bg-variant(@state-success-bg);\n}\n.bg-info {\n .bg-variant(@state-info-bg);\n}\n.bg-warning {\n .bg-variant(@state-warning-bg);\n}\n.bg-danger {\n .bg-variant(@state-danger-bg);\n}\n\n\n// Page header\n// -------------------------\n\n.page-header {\n padding-bottom: ((@line-height-computed / 2) - 1);\n margin: (@line-height-computed * 2) 0 @line-height-computed;\n border-bottom: 1px solid @page-header-border-color;\n}\n\n\n// Lists\n// -------------------------\n\n// Unordered and Ordered lists\nul,\nol {\n margin-top: 0;\n margin-bottom: (@line-height-computed / 2);\n ul,\n ol {\n margin-bottom: 0;\n }\n}\n\n// List options\n\n// Unstyled keeps list items block level, just removes default browser padding and list-style\n.list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n\n// Inline turns list items into inline-block\n.list-inline {\n .list-unstyled();\n margin-left: -5px;\n\n > li {\n display: inline-block;\n padding-left: 5px;\n padding-right: 5px;\n }\n}\n\n// Description Lists\ndl {\n margin-top: 0; // Remove browser default\n margin-bottom: @line-height-computed;\n}\ndt,\ndd {\n line-height: @line-height-base;\n}\ndt {\n font-weight: bold;\n}\ndd {\n margin-left: 0; // Undo browser default\n}\n\n// Horizontal description lists\n//\n// Defaults to being stacked without any of the below styles applied, until the\n// grid breakpoint is reached (default of ~768px).\n\n.dl-horizontal {\n dd {\n &:extend(.clearfix all); // Clear the floated `dt` if an empty `dd` is present\n }\n\n @media (min-width: @grid-float-breakpoint) {\n dt {\n float: left;\n width: (@dl-horizontal-offset - 20);\n clear: left;\n text-align: right;\n .text-overflow();\n }\n dd {\n margin-left: @dl-horizontal-offset;\n }\n }\n}\n\n\n// Misc\n// -------------------------\n\n// Abbreviations and acronyms\nabbr[title],\n// Add data-* attribute to help out our tooltip plugin, per https://github.com/twbs/bootstrap/issues/5257\nabbr[data-original-title] {\n cursor: help;\n border-bottom: 1px dotted @abbr-border-color;\n}\n.initialism {\n font-size: 90%;\n text-transform: uppercase;\n}\n\n// Blockquotes\nblockquote {\n padding: (@line-height-computed / 2) @line-height-computed;\n margin: 0 0 @line-height-computed;\n font-size: @blockquote-font-size;\n border-left: 5px solid @blockquote-border-color;\n\n p,\n ul,\n ol {\n &:last-child {\n margin-bottom: 0;\n }\n }\n\n // Note: Deprecated small and .small as of v3.1.0\n // Context: https://github.com/twbs/bootstrap/issues/11660\n footer,\n small,\n .small {\n display: block;\n font-size: 80%; // back to default font-size\n line-height: @line-height-base;\n color: @blockquote-small-color;\n\n &:before {\n content: '\\2014 \\00A0'; // em dash, nbsp\n }\n }\n}\n\n// Opposite alignment of blockquote\n//\n// Heads up: `blockquote.pull-right` has been deprecated as of v3.1.0.\n.blockquote-reverse,\nblockquote.pull-right {\n padding-right: 15px;\n padding-left: 0;\n border-right: 5px solid @blockquote-border-color;\n border-left: 0;\n text-align: right;\n\n // Account for citation\n footer,\n small,\n .small {\n &:before { content: ''; }\n &:after {\n content: '\\00A0 \\2014'; // nbsp, em dash\n }\n }\n}\n\n// Quotes\nblockquote:before,\nblockquote:after {\n content: \"\";\n}\n\n// Addresses\naddress {\n margin-bottom: @line-height-computed;\n font-style: normal;\n line-height: @line-height-base;\n}\n","// Typography\n\n.text-emphasis-variant(@color) {\n color: @color;\n a&:hover {\n color: darken(@color, 10%);\n }\n}\n","// Contextual backgrounds\n\n.bg-variant(@color) {\n background-color: @color;\n a&:hover {\n background-color: darken(@color, 10%);\n }\n}\n","// Text overflow\n// Requires inline-block or block for proper styling\n\n.text-overflow() {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n","//\n// Code (inline and block)\n// --------------------------------------------------\n\n\n// Inline and block code styles\ncode,\nkbd,\npre,\nsamp {\n font-family: @font-family-monospace;\n}\n\n// Inline code\ncode {\n padding: 2px 4px;\n font-size: 90%;\n color: @code-color;\n background-color: @code-bg;\n border-radius: @border-radius-base;\n}\n\n// User input typically entered via keyboard\nkbd {\n padding: 2px 4px;\n font-size: 90%;\n color: @kbd-color;\n background-color: @kbd-bg;\n border-radius: @border-radius-small;\n box-shadow: inset 0 -1px 0 rgba(0,0,0,.25);\n\n kbd {\n padding: 0;\n font-size: 100%;\n box-shadow: none;\n }\n}\n\n// Blocks of code\npre {\n display: block;\n padding: ((@line-height-computed - 1) / 2);\n margin: 0 0 (@line-height-computed / 2);\n font-size: (@font-size-base - 1); // 14px to 13px\n line-height: @line-height-base;\n word-break: break-all;\n word-wrap: break-word;\n color: @pre-color;\n background-color: @pre-bg;\n border: 1px solid @pre-border-color;\n border-radius: @border-radius-base;\n\n // Account for some code outputs that place code tags in pre tags\n code {\n padding: 0;\n font-size: inherit;\n color: inherit;\n white-space: pre-wrap;\n background-color: transparent;\n border-radius: 0;\n }\n}\n\n// Enable scrollable blocks of code\n.pre-scrollable {\n max-height: @pre-scrollable-max-height;\n overflow-y: scroll;\n}\n","//\n// Grid system\n// --------------------------------------------------\n\n\n// Container widths\n//\n// Set the container width, and override it for fixed navbars in media queries.\n\n.container {\n .container-fixed();\n\n @media (min-width: @screen-sm-min) {\n width: @container-sm;\n }\n @media (min-width: @screen-md-min) {\n width: @container-md;\n }\n @media (min-width: @screen-lg-min) {\n width: @container-lg;\n }\n}\n\n\n// Fluid container\n//\n// Utilizes the mixin meant for fixed width containers, but without any defined\n// width for fluid, full width layouts.\n\n.container-fluid {\n .container-fixed();\n}\n\n\n// Row\n//\n// Rows contain and clear the floats of your columns.\n\n.row {\n .make-row();\n}\n\n\n// Columns\n//\n// Common styles for small and large grid columns\n\n.make-grid-columns();\n\n\n// Extra small grid\n//\n// Columns, offsets, pushes, and pulls for extra small devices like\n// smartphones.\n\n.make-grid(xs);\n\n\n// Small grid\n//\n// Columns, offsets, pushes, and pulls for the small device range, from phones\n// to tablets.\n\n@media (min-width: @screen-sm-min) {\n .make-grid(sm);\n}\n\n\n// Medium grid\n//\n// Columns, offsets, pushes, and pulls for the desktop device range.\n\n@media (min-width: @screen-md-min) {\n .make-grid(md);\n}\n\n\n// Large grid\n//\n// Columns, offsets, pushes, and pulls for the large desktop device range.\n\n@media (min-width: @screen-lg-min) {\n .make-grid(lg);\n}\n","// Grid system\n//\n// Generate semantic grid columns with these mixins.\n\n// Centered container element\n.container-fixed(@gutter: @grid-gutter-width) {\n margin-right: auto;\n margin-left: auto;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n &:extend(.clearfix all);\n}\n\n// Creates a wrapper for a series of columns\n.make-row(@gutter: @grid-gutter-width) {\n margin-left: (@gutter / -2);\n margin-right: (@gutter / -2);\n &:extend(.clearfix all);\n}\n\n// Generate the extra small columns\n.make-xs-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n float: left;\n width: percentage((@columns / @grid-columns));\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n}\n.make-xs-column-offset(@columns) {\n margin-left: percentage((@columns / @grid-columns));\n}\n.make-xs-column-push(@columns) {\n left: percentage((@columns / @grid-columns));\n}\n.make-xs-column-pull(@columns) {\n right: percentage((@columns / @grid-columns));\n}\n\n// Generate the small columns\n.make-sm-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n\n @media (min-width: @screen-sm-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-offset(@columns) {\n @media (min-width: @screen-sm-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-push(@columns) {\n @media (min-width: @screen-sm-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-pull(@columns) {\n @media (min-width: @screen-sm-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n\n// Generate the medium columns\n.make-md-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n\n @media (min-width: @screen-md-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-offset(@columns) {\n @media (min-width: @screen-md-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-push(@columns) {\n @media (min-width: @screen-md-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-pull(@columns) {\n @media (min-width: @screen-md-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n\n// Generate the large columns\n.make-lg-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n\n @media (min-width: @screen-lg-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-offset(@columns) {\n @media (min-width: @screen-lg-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-push(@columns) {\n @media (min-width: @screen-lg-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-pull(@columns) {\n @media (min-width: @screen-lg-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n","// Framework grid generation\n//\n// Used only by Bootstrap to generate the correct number of grid classes given\n// any value of `@grid-columns`.\n\n.make-grid-columns() {\n // Common styles for all sizes of grid columns, widths 1-12\n .col(@index) when (@index = 1) { // initial\n @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n .col((@index + 1), @item);\n }\n .col(@index, @list) when (@index =< @grid-columns) { // general; \"=<\" isn't a typo\n @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n .col((@index + 1), ~\"@{list}, @{item}\");\n }\n .col(@index, @list) when (@index > @grid-columns) { // terminal\n @{list} {\n position: relative;\n // Prevent columns from collapsing when empty\n min-height: 1px;\n // Inner gutter via padding\n padding-left: (@grid-gutter-width / 2);\n padding-right: (@grid-gutter-width / 2);\n }\n }\n .col(1); // kickstart it\n}\n\n.float-grid-columns(@class) {\n .col(@index) when (@index = 1) { // initial\n @item: ~\".col-@{class}-@{index}\";\n .col((@index + 1), @item);\n }\n .col(@index, @list) when (@index =< @grid-columns) { // general\n @item: ~\".col-@{class}-@{index}\";\n .col((@index + 1), ~\"@{list}, @{item}\");\n }\n .col(@index, @list) when (@index > @grid-columns) { // terminal\n @{list} {\n float: left;\n }\n }\n .col(1); // kickstart it\n}\n\n.calc-grid-column(@index, @class, @type) when (@type = width) and (@index > 0) {\n .col-@{class}-@{index} {\n width: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = push) and (@index > 0) {\n .col-@{class}-push-@{index} {\n left: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = push) and (@index = 0) {\n .col-@{class}-push-0 {\n left: auto;\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index > 0) {\n .col-@{class}-pull-@{index} {\n right: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index = 0) {\n .col-@{class}-pull-0 {\n right: auto;\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = offset) {\n .col-@{class}-offset-@{index} {\n margin-left: percentage((@index / @grid-columns));\n }\n}\n\n// Basic looping in LESS\n.loop-grid-columns(@index, @class, @type) when (@index >= 0) {\n .calc-grid-column(@index, @class, @type);\n // next iteration\n .loop-grid-columns((@index - 1), @class, @type);\n}\n\n// Create grid for specific class\n.make-grid(@class) {\n .float-grid-columns(@class);\n .loop-grid-columns(@grid-columns, @class, width);\n .loop-grid-columns(@grid-columns, @class, pull);\n .loop-grid-columns(@grid-columns, @class, push);\n .loop-grid-columns(@grid-columns, @class, offset);\n}\n","//\n// Tables\n// --------------------------------------------------\n\n\ntable {\n background-color: @table-bg;\n}\nth {\n text-align: left;\n}\n\n\n// Baseline styles\n\n.table {\n width: 100%;\n max-width: 100%;\n margin-bottom: @line-height-computed;\n // Cells\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n padding: @table-cell-padding;\n line-height: @line-height-base;\n vertical-align: top;\n border-top: 1px solid @table-border-color;\n }\n }\n }\n // Bottom align for column headings\n > thead > tr > th {\n vertical-align: bottom;\n border-bottom: 2px solid @table-border-color;\n }\n // Remove top border from thead by default\n > caption + thead,\n > colgroup + thead,\n > thead:first-child {\n > tr:first-child {\n > th,\n > td {\n border-top: 0;\n }\n }\n }\n // Account for multiple tbody instances\n > tbody + tbody {\n border-top: 2px solid @table-border-color;\n }\n\n // Nesting\n .table {\n background-color: @body-bg;\n }\n}\n\n\n// Condensed table w/ half padding\n\n.table-condensed {\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n padding: @table-condensed-cell-padding;\n }\n }\n }\n}\n\n\n// Bordered version\n//\n// Add borders all around the table and between all the columns.\n\n.table-bordered {\n border: 1px solid @table-border-color;\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n border: 1px solid @table-border-color;\n }\n }\n }\n > thead > tr {\n > th,\n > td {\n border-bottom-width: 2px;\n }\n }\n}\n\n\n// Zebra-striping\n//\n// Default zebra-stripe styles (alternating gray and transparent backgrounds)\n\n.table-striped {\n > tbody > tr:nth-child(odd) {\n > td,\n > th {\n background-color: @table-bg-accent;\n }\n }\n}\n\n\n// Hover effect\n//\n// Placed here since it has to come after the potential zebra striping\n\n.table-hover {\n > tbody > tr:hover {\n > td,\n > th {\n background-color: @table-bg-hover;\n }\n }\n}\n\n\n// Table cell sizing\n//\n// Reset default table behavior\n\ntable col[class*=\"col-\"] {\n position: static; // Prevent border hiding in Firefox and IE9/10 (see https://github.com/twbs/bootstrap/issues/11623)\n float: none;\n display: table-column;\n}\ntable {\n td,\n th {\n &[class*=\"col-\"] {\n position: static; // Prevent border hiding in Firefox and IE9/10 (see https://github.com/twbs/bootstrap/issues/11623)\n float: none;\n display: table-cell;\n }\n }\n}\n\n\n// Table backgrounds\n//\n// Exact selectors below required to override `.table-striped` and prevent\n// inheritance to nested tables.\n\n// Generate the contextual variants\n.table-row-variant(active; @table-bg-active);\n.table-row-variant(success; @state-success-bg);\n.table-row-variant(info; @state-info-bg);\n.table-row-variant(warning; @state-warning-bg);\n.table-row-variant(danger; @state-danger-bg);\n\n\n// Responsive tables\n//\n// Wrap your tables in `.table-responsive` and we'll make them mobile friendly\n// by enabling horizontal scrolling. Only applies <768px. Everything above that\n// will display normally.\n\n.table-responsive {\n @media screen and (max-width: @screen-xs-max) {\n width: 100%;\n margin-bottom: (@line-height-computed * 0.75);\n overflow-y: hidden;\n overflow-x: auto;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n border: 1px solid @table-border-color;\n -webkit-overflow-scrolling: touch;\n\n // Tighten up spacing\n > .table {\n margin-bottom: 0;\n\n // Ensure the content doesn't wrap\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n white-space: nowrap;\n }\n }\n }\n }\n\n // Special overrides for the bordered tables\n > .table-bordered {\n border: 0;\n\n // Nuke the appropriate borders so that the parent can handle them\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th:first-child,\n > td:first-child {\n border-left: 0;\n }\n > th:last-child,\n > td:last-child {\n border-right: 0;\n }\n }\n }\n\n // Only nuke the last row's bottom-border in `tbody` and `tfoot` since\n // chances are there will be only one `tr` in a `thead` and that would\n // remove the border altogether.\n > tbody,\n > tfoot {\n > tr:last-child {\n > th,\n > td {\n border-bottom: 0;\n }\n }\n }\n\n }\n }\n}\n","// Tables\n\n.table-row-variant(@state; @background) {\n // Exact selectors below required to override `.table-striped` and prevent\n // inheritance to nested tables.\n .table > thead > tr,\n .table > tbody > tr,\n .table > tfoot > tr {\n > td.@{state},\n > th.@{state},\n &.@{state} > td,\n &.@{state} > th {\n background-color: @background;\n }\n }\n\n // Hover states for `.table-hover`\n // Note: this is not available for cells or rows within `thead` or `tfoot`.\n .table-hover > tbody > tr {\n > td.@{state}:hover,\n > th.@{state}:hover,\n &.@{state}:hover > td,\n &:hover > .@{state},\n &.@{state}:hover > th {\n background-color: darken(@background, 5%);\n }\n }\n}\n","//\n// Forms\n// --------------------------------------------------\n\n\n// Normalize non-controls\n//\n// Restyle and baseline non-control form elements.\n\nfieldset {\n padding: 0;\n margin: 0;\n border: 0;\n // Chrome and Firefox set a `min-width: min-content;` on fieldsets,\n // so we reset that to ensure it behaves more like a standard block element.\n // See https://github.com/twbs/bootstrap/issues/12359.\n min-width: 0;\n}\n\nlegend {\n display: block;\n width: 100%;\n padding: 0;\n margin-bottom: @line-height-computed;\n font-size: (@font-size-base * 1.5);\n line-height: inherit;\n color: @legend-color;\n border: 0;\n border-bottom: 1px solid @legend-border-color;\n}\n\nlabel {\n display: inline-block;\n max-width: 100%; // Force IE8 to wrap long content (see https://github.com/twbs/bootstrap/issues/13141)\n margin-bottom: 5px;\n font-weight: bold;\n}\n\n\n// Normalize form controls\n//\n// While most of our form styles require extra classes, some basic normalization\n// is required to ensure optimum display with or without those classes to better\n// address browser inconsistencies.\n\n// Override content-box in Normalize (* isn't specific enough)\ninput[type=\"search\"] {\n .box-sizing(border-box);\n}\n\n// Position radios and checkboxes better\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n margin: 4px 0 0;\n margin-top: 1px \\9; // IE8-9\n line-height: normal;\n}\n\n// Set the height of file controls to match text inputs\ninput[type=\"file\"] {\n display: block;\n}\n\n// Make range inputs behave like textual form controls\ninput[type=\"range\"] {\n display: block;\n width: 100%;\n}\n\n// Make multiple select elements height not fixed\nselect[multiple],\nselect[size] {\n height: auto;\n}\n\n// Focus for file, radio, and checkbox\ninput[type=\"file\"]:focus,\ninput[type=\"radio\"]:focus,\ninput[type=\"checkbox\"]:focus {\n .tab-focus();\n}\n\n// Adjust output element\noutput {\n display: block;\n padding-top: (@padding-base-vertical + 1);\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @input-color;\n}\n\n\n// Common form controls\n//\n// Shared size and type resets for form controls. Apply `.form-control` to any\n// of the following form controls:\n//\n// select\n// textarea\n// input[type=\"text\"]\n// input[type=\"password\"]\n// input[type=\"datetime\"]\n// input[type=\"datetime-local\"]\n// input[type=\"date\"]\n// input[type=\"month\"]\n// input[type=\"time\"]\n// input[type=\"week\"]\n// input[type=\"number\"]\n// input[type=\"email\"]\n// input[type=\"url\"]\n// input[type=\"search\"]\n// input[type=\"tel\"]\n// input[type=\"color\"]\n\n.form-control {\n display: block;\n width: 100%;\n height: @input-height-base; // Make inputs at least the height of their button counterpart (base line-height + padding + border)\n padding: @padding-base-vertical @padding-base-horizontal;\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @input-color;\n background-color: @input-bg;\n background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n border: 1px solid @input-border;\n border-radius: @input-border-radius;\n .box-shadow(inset 0 1px 1px rgba(0,0,0,.075));\n .transition(~\"border-color ease-in-out .15s, box-shadow ease-in-out .15s\");\n\n // Customize the `:focus` state to imitate native WebKit styles.\n .form-control-focus();\n\n // Placeholder\n .placeholder();\n\n // Disabled and read-only inputs\n //\n // HTML5 says that controls under a fieldset > legend:first-child won't be\n // disabled if the fieldset is disabled. Due to implementation difficulty, we\n // don't honor that edge case; we style them as disabled anyway.\n &[disabled],\n &[readonly],\n fieldset[disabled] & {\n cursor: not-allowed;\n background-color: @input-bg-disabled;\n opacity: 1; // iOS fix for unreadable disabled content\n }\n\n // Reset height for `textarea`s\n textarea& {\n height: auto;\n }\n}\n\n\n// Search inputs in iOS\n//\n// This overrides the extra rounded corners on search inputs in iOS so that our\n// `.form-control` class can properly style them. Note that this cannot simply\n// be added to `.form-control` as it's not specific enough. For details, see\n// https://github.com/twbs/bootstrap/issues/11586.\n\ninput[type=\"search\"] {\n -webkit-appearance: none;\n}\n\n\n// Special styles for iOS temporal inputs\n//\n// In Mobile Safari, setting `display: block` on temporal inputs causes the\n// text within the input to become vertically misaligned.\n// As a workaround, we set a pixel line-height that matches the\n// given height of the input. Since this fucks up everything else, we have to\n// appropriately reset it for Internet Explorer and the size variations.\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n line-height: @input-height-base;\n // IE8+ misaligns the text within date inputs, so we reset\n line-height: @line-height-base ~\"\\0\";\n\n &.input-sm {\n line-height: @input-height-small;\n }\n &.input-lg {\n line-height: @input-height-large;\n }\n}\n\n\n// Form groups\n//\n// Designed to help with the organization and spacing of vertical forms. For\n// horizontal forms, use the predefined grid classes.\n\n.form-group {\n margin-bottom: 15px;\n}\n\n\n// Checkboxes and radios\n//\n// Indent the labels to position radios/checkboxes as hanging controls.\n\n.radio,\n.checkbox {\n position: relative;\n display: block;\n min-height: @line-height-computed; // clear the floating input if there is no label text\n margin-top: 10px;\n margin-bottom: 10px;\n\n label {\n padding-left: 20px;\n margin-bottom: 0;\n font-weight: normal;\n cursor: pointer;\n }\n}\n.radio input[type=\"radio\"],\n.radio-inline input[type=\"radio\"],\n.checkbox input[type=\"checkbox\"],\n.checkbox-inline input[type=\"checkbox\"] {\n position: absolute;\n margin-left: -20px;\n margin-top: 4px \\9;\n}\n\n.radio + .radio,\n.checkbox + .checkbox {\n margin-top: -5px; // Move up sibling radios or checkboxes for tighter spacing\n}\n\n// Radios and checkboxes on same line\n.radio-inline,\n.checkbox-inline {\n display: inline-block;\n padding-left: 20px;\n margin-bottom: 0;\n vertical-align: middle;\n font-weight: normal;\n cursor: pointer;\n}\n.radio-inline + .radio-inline,\n.checkbox-inline + .checkbox-inline {\n margin-top: 0;\n margin-left: 10px; // space out consecutive inline controls\n}\n\n// Apply same disabled cursor tweak as for inputs\n// Some special care is needed because