Hal De 3 年之前
当前提交
daa0ee9fb8
共有 10 个文件被更改,包括 730 次插入0 次删除
  1. 46 0
      .gitignore
  2. 23 0
      .goreleaser.yml
  3. 25 0
      Dockerfile
  4. 201 0
      LICENSE
  5. 24 0
      README.md
  6. 14 0
      configs/freepbx-tftp-server.service
  7. 144 0
      configs/sep-cisco.cnf.xml.tpl
  8. 9 0
      go.mod
  9. 26 0
      go.sum
  10. 218 0
      main.go

+ 46 - 0
.gitignore

@@ -0,0 +1,46 @@
+# Binaries for programs and plugins
+*.exe
+*.exe~
+*.dll
+*.so
+*.dylib
+
+# Test binary, built with `go test -c`
+*.test
+
+# Output of the go coverage tool, specifically when used with LiteIDE
+*.out
+
+*.conf
+
+# Dependency directories (remove the comment below to include it)
+# vendor/
+
+# General
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+freebpx-tftp-server-mac

+ 23 - 0
.goreleaser.yml

@@ -0,0 +1,23 @@
+env:
+  - GO111MODULE=on
+  - GOPROXY=https://goproxy.io
+
+before:
+  hooks:
+    - go mod download
+
+builds:
+  -
+    env:
+      - CGO_ENABLED=0
+    goos:
+      - linux
+    goarch:
+      - amd64
+
+archives:
+  - files:
+    - none*
+
+checksum:
+  name_template: 'checksums.txt'

+ 25 - 0
Dockerfile

@@ -0,0 +1,25 @@
+ARG GOVERSION=latest
+FROM golang:$GOVERSION AS builder
+
+WORKDIR /src
+COPY . .
+
+ENV CGO_ENABLED=0
+ENV GO111MODULE=on
+ENV GOOS=linux
+ENV GOARCH=amd64
+RUN rm -f freepbx-tftp-server && go build -a -tags netgo -ldflags '-w -extldflags "-static"' -o freepbx-tftp-server main.go
+
+FROM scratch
+
+EXPOSE 69/udp
+
+VOLUME ["/tftpboot"]
+
+COPY --from=builder /src/freepbx-tftp-server /freepbx-tftp-server
+#COPY --from=builder /src/script/docker-entrypoint.sh /bin/entrypoint.sh
+
+ENV HOME=/tftpboot
+
+CMD ["/freepbx-tftp-server"]
+#ENTRYPOINT ["/bin/entrypoint.sh", "/bin/syncthing", "-home", "/var/syncthing/config"]

+ 201 - 0
LICENSE

@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 24 - 0
README.md

@@ -0,0 +1,24 @@
+## FreePBX tftp server
+
+This is an advanced tftp server that automatically generates config files for sip phones.
+
+## How it works
+
+FreePBX server syncs with Active Directory(AD). Binding users to phones is carried out through the fax field in the user properties from AD. The tftp server receives a request for a file of the form `SEP<MAC>.cfg.xml`, makes a request to the freepbx database, generates a config and sends it to the phone.
+
+## Requirements:
+
+* [go >= 1.16](https://golang.org)
+* [FreePBX >= 15](https://www.freepbx.org)
+
+## Flags:
+
+```bash
+Usage of ./freebpx-tftp-server:
+  -freepbx-conf string
+    	Set path to freepbx db connection config file (default "/etc/freepbx.conf")
+  -sep-template-file string
+    	Set path to sep template file (default "./sep-cisco.cnf.xml.tpl")
+  -workdir string
+    	Set working directory (default "/tftpboot")
+```

+ 14 - 0
configs/freepbx-tftp-server.service

@@ -0,0 +1,14 @@
+[Unit]
+Description=FreePBX tftp server
+After=network.target
+
+[Service]
+Type=simple
+Restart=on-failure
+RestartSec=10
+
+WorkingDirectory=/opt/freepbx-tftp-server
+ExecStart=/opt/freepbx-tftp-server/freepbx-tftp-server -workdir=/tftpboot
+
+[Install]
+WantedBy=multi-user.target

+ 144 - 0
configs/sep-cisco.cnf.xml.tpl

@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<device>
+   <devicePool>
+      <dateTimeSetting>
+         <dateTemplate>D.M.Y</dateTemplate>
+         <timeZone>E. Europe Standard/Daylight Time</timeZone>
+         <ntps>
+            <ntp>
+               <name>time.windows.com</name>
+               <ntpMode>Unicast</ntpMode>
+            </ntp>
+         </ntps>
+      </dateTimeSetting>
+      <callManagerGroup>
+         <members>
+            <member priority="0">
+               <callManager>
+                  <ports>
+                     <ethernetPhonePort>2000</ethernetPhonePort>
+                  </ports>
+                  <processNodeName>10.0.201.254</processNodeName>
+               </callManager>
+            </member>
+         </members>
+      </callManagerGroup>
+   </devicePool>
+   <commonProfile>
+      <callLogBlfEnabled>3</callLogBlfEnabled>
+   </commonProfile>
+   <loadInformation>SIP6945.9-4-1-3</loadInformation>
+   <userLocale>
+      <name>Russian_Russia</name>
+      <uid />
+      <langCode>ru_RU</langCode>
+      <version />
+      <winCharSet>utf-8</winCharSet>
+   </userLocale>
+   <networkLocale>United_States</networkLocale>
+   <networkLocaleInfo>
+      <name>Russian_Russia</name>
+   </networkLocaleInfo>
+   <idleTimeout>0</idleTimeout>
+   <authenticationURL />
+   <directoryURL />
+   <idleURL />
+   <informationURL />
+   <messagesURL />
+   <proxyServerURL />
+   <servicesURL />
+   <capfAuthMode>0</capfAuthMode>
+   <capfList>
+      <capf>
+         <phonePort>5060</phonePort>
+         <processNodeName />
+      </capf>
+   </capfList>
+   <deviceSecurityMode>1</deviceSecurityMode>
+   <sipProfile>
+      <sipCallFeatures>
+         <cnfJoinEnabled>true</cnfJoinEnabled>
+         <callForwardURI>x--serviceuri-cfwdall</callForwardURI>
+         <callPickupURI>x-cisco-serviceuri-pickup</callPickupURI>
+         <callPickupListURI>x-cisco-serviceuri-opickup</callPickupListURI>
+         <callPickupGroupURI>x-cisco-serviceuri-gpickup</callPickupGroupURI>
+         <meetMeServiceURI>x-cisco-serviceuri-meetme</meetMeServiceURI>
+         <abbreviatedDialURI>x-cisco-serviceuri-abbrdial</abbreviatedDialURI>
+         <rfc2543Hold>true</rfc2543Hold>
+         <callHoldRingback>2</callHoldRingback>
+         <localCfwdEnable>true</localCfwdEnable>
+         <semiAttendedTransfer>true</semiAttendedTransfer>
+         <anonymousCallBlock>2</anonymousCallBlock>
+         <callerIdBlocking>0</callerIdBlocking>
+         <dndControl>0</dndControl>
+         <remoteCcEnable>true</remoteCcEnable>
+      </sipCallFeatures>
+      <sipStack>
+         <sipInviteRetx>6</sipInviteRetx>
+         <sipRetx>10</sipRetx>
+         <timerInviteExpires>180</timerInviteExpires>
+         <timerRegisterExpires>120</timerRegisterExpires>
+         <timerRegisterDelta>5</timerRegisterDelta>
+         <timerKeepAliveExpires>120</timerKeepAliveExpires>
+         <timerSubscribeExpires>120</timerSubscribeExpires>
+         <timerSubscribeDelta>5</timerSubscribeDelta>
+         <timerT1>500</timerT1>
+         <timerT2>4000</timerT2>
+         <maxRedirects>70</maxRedirects>
+         <remotePartyID>false</remotePartyID>
+         <userInfo>None</userInfo>
+      </sipStack>
+      <autoAnswerTimer>1</autoAnswerTimer>
+      <autoAnswerAltBehavior>false</autoAnswerAltBehavior>
+      <autoAnswerOverride>true</autoAnswerOverride>
+      <transferOnhookEnabled>true</transferOnhookEnabled>
+      <enableVad>false</enableVad>
+      <preferredCodec>g729</preferredCodec>
+      <dtmfAvtPayload>101</dtmfAvtPayload>
+      <dtmfDbLevel>3</dtmfDbLevel>
+      <dtmfOutofBand>avt</dtmfOutofBand>
+      <alwaysUsePrimeLine>false</alwaysUsePrimeLine>
+      <alwaysUsePrimeLineVoiceMail>false</alwaysUsePrimeLineVoiceMail>
+      <kpml>3</kpml>
+      <stutterMsgWaiting>1</stutterMsgWaiting>
+      <callStats>false</callStats>
+      <silentPeriodBetweenCallWaitingBursts>10</silentPeriodBetweenCallWaitingBursts>
+      <disableLocalSpeedDialConfig>false</disableLocalSpeedDialConfig>
+      <startMediaPort>16384</startMediaPort>
+      <stopMediaPort>16399</stopMediaPort>
+      <voipControlPort>5069</voipControlPort>
+      <dscpForAudio>184</dscpForAudio>
+      <ringSettingBusyStationPolicy>0</ringSettingBusyStationPolicy>
+      <dialTemplate>dialplan.xml</dialTemplate>
+      <phoneLabel>Office</phoneLabel>
+      <sipLines>
+         <line button="1">
+            <featureID>9</featureID>
+            <featureLabel>CIT Networks</featureLabel>
+            <name>{{.PhoneNumber}}</name>
+            <displayName>{{.DisplayName}}</displayName>
+            <contact>{{.PhoneNumber}}</contact>
+            <proxy>10.0.201.254</proxy>
+            <port>5060</port>
+            <autoAnswer>
+               <autoAnswerEnabled>2</autoAnswerEnabled>
+            </autoAnswer>
+            <callWaiting>3</callWaiting>
+            <authName>{{.PhoneNumber}}</authName>
+            <authPassword>{{.PhonePassword}}</authPassword>
+            <sharedLine>false</sharedLine>
+            <messageWaitingLampPolicy>1</messageWaitingLampPolicy>
+            <messagesNumber>120</messagesNumber>
+            <ringSettingIdle>4</ringSettingIdle>
+            <ringSettingActive>5</ringSettingActive>
+            <forwardCallInfoDisplay>
+               <callerName>true</callerName>
+               <callerNumber>false</callerNumber>
+               <redirectedNumber>false</redirectedNumber>
+               <dialedNumber>true</dialedNumber>
+            </forwardCallInfoDisplay>
+         </line>
+      </sipLines>
+   </sipProfile>
+<directoryURL>http://192.168.0.54/directory.xml</directoryURL>
+</device>

+ 9 - 0
go.mod

@@ -0,0 +1,9 @@
+module freepbx-tftp-server
+
+go 1.16
+
+require (
+	github.com/go-sql-driver/mysql v1.5.0
+	github.com/namsral/flag v1.7.4-pre.0.20170814194028-67f268f20922
+	github.com/pin/tftp v0.0.0-20200229063000-e4f073737eb2
+)

+ 26 - 0
go.sum

@@ -0,0 +1,26 @@
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
+github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/namsral/flag v1.7.4-pre.0.20170814194028-67f268f20922 h1:LdiOM0EFQG9RgQJGhrW1GL0qpfSQU28FNppDFyKjH8g=
+github.com/namsral/flag v1.7.4-pre.0.20170814194028-67f268f20922/go.mod h1:OXldTctbM6SWH1K899kPZcf65KxJiD7MsceFUpB5yDo=
+github.com/pin/tftp v0.0.0-20200229063000-e4f073737eb2 h1:gdzFBthLhDNr1+CXXEskaYh3ugPjEtoLyn0m/AjNRWw=
+github.com/pin/tftp v0.0.0-20200229063000-e4f073737eb2/go.mod h1:1kbtV8n0I3ujA7FewmPymdTyq7Lgk5UhmEy7pcrVVWU=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

+ 218 - 0
main.go

@@ -0,0 +1,218 @@
+package main
+
+import (
+  "bytes"
+  "database/sql"
+  "fmt"
+  "io"
+  "io/ioutil"
+  "os"
+  "path"
+  "regexp"
+  "strings"
+  "text/template"
+
+  _ "github.com/go-sql-driver/mysql"
+  "github.com/pin/tftp"
+  "github.com/namsral/flag"
+)
+
+var (
+  workdir, sepTeplateFilePath, freepbxConf string
+  sepTeplateFile                           []byte
+  db                                       *sql.DB
+)
+
+//PhoneSetting struct
+type PhoneSetting struct {
+  DisplayName, PhonePassword string
+  PhoneNumber                string
+}
+
+func getDBConnectionParams() (string, error) {
+  var con string
+  rex := regexp.MustCompile(`\["(.*)"\] = "(.*)";`)
+  buf := new(bytes.Buffer)
+
+  file, err := os.Open(freepbxConf)
+  if err != nil {
+    return con, err
+  }
+  defer file.Close()
+
+  buf.ReadFrom(file)
+
+  data := rex.FindAllStringSubmatch(buf.String(), -1)
+
+  res := make(map[string]string)
+  for _, kv := range data {
+    k := kv[1]
+    v := kv[2]
+    res[k] = v
+  }
+
+  con = fmt.Sprintf("%s:%s@tcp(%s)/%s", res["AMPDBUSER"], res["AMPDBPASS"], res["AMPDBHOST"], res["AMPDBNAME"])
+
+  return con, nil
+}
+
+//Getting phone setting from freepbx database
+func getPhoneSetting(filename string) (*PhoneSetting, error) {
+  var ps PhoneSetting
+
+  filename = strings.TrimSuffix(filename, ".cnf.xml")
+  query := `
+    SELECT userman_users.displayname, sip.data, userman_users.default_extension
+      FROM userman_users
+      LEFT JOIN sip
+      ON userman_users.default_extension=sip.id AND sip.keyword='secret'
+      WHERE userman_users.fax=?
+      ORDER BY userman_users.default_extension
+    LIMIT 1
+    `
+
+  err := db.QueryRow(query, filename).Scan(&ps.DisplayName, &ps.PhonePassword, &ps.PhoneNumber)
+
+  if err != nil {
+    return &ps, err
+  }
+
+  return &ps, nil
+}
+
+//Send file to client
+func sendFile(file *bytes.Buffer, rf io.ReaderFrom) error {
+  n, err := rf.ReadFrom(file)
+
+  if err != nil {
+    fmt.Fprintf(os.Stderr, "%v\n", err)
+    return err
+  }
+  fmt.Printf("%d bytes sent\n", n)
+  return nil
+}
+
+//Reading file in tftp dir. If file not found, returning empty file
+func readFile(filename string, rf io.ReaderFrom) error {
+  var err error
+
+  buf := new(bytes.Buffer)
+
+  file, err := os.Open(path.Join(workdir, filename))
+  if err != nil {
+    fmt.Fprintf(os.Stderr, "%v\n", err)
+  } else {
+    buf.ReadFrom(file)
+  }
+  defer file.Close()
+
+  err = sendFile(buf, rf)
+  if err != nil {
+    fmt.Fprintf(os.Stderr, "%v\n", err)
+    return err
+  }
+
+  return nil
+}
+
+//Generating phone settings file use go-template
+func genFile(filename string, rf io.ReaderFrom) error {
+  var (
+    tpl bytes.Buffer
+    err error
+  )
+
+  p, err := getPhoneSetting(filename)
+  if err != nil {
+    fmt.Fprintf(os.Stderr, "%v\n", err)
+    return err
+  }
+
+  t := template.Must(template.New("sepTeplateFile").Parse(string(sepTeplateFile)))
+
+  err = t.Execute(&tpl, p)
+  if err != nil {
+    fmt.Fprintf(os.Stderr, "%v\n", err)
+    return err
+  }
+
+  err = sendFile(&tpl, rf)
+  if err != nil {
+    fmt.Fprintf(os.Stderr, "%v\n", err)
+    return err
+  }
+
+  return nil
+}
+
+//Processing file request from a tftp client
+func readHandler(filename string, rf io.ReaderFrom) error {
+  raddr := rf.(tftp.OutgoingTransfer).RemoteAddr()
+  laddr := rf.(tftp.RequestPacketInfo).LocalIP()
+  fmt.Println("RRQ from:", raddr.String(), "To:", laddr.String(), "File:", filename)
+
+  sepFile, err := path.Match("SEP*.cnf.xml", filename)
+  if err != nil {
+    fmt.Fprintf(os.Stderr, "%v\n", err)
+    return err
+  }
+
+  if sepFile {
+    genFile(filename, rf)
+  } else {
+    readFile(filename, rf)
+  }
+
+  return nil
+}
+
+//Declaring cli flags
+func init() {
+  flag.StringVar(&workdir, "workdir", "/tftpboot", "Set working directory")
+  flag.StringVar(&sepTeplateFilePath, "sep-template-file", "./sep-cisco.cnf.xml.tpl", "Set path to sep template file")
+  flag.StringVar(&freepbxConf, "freepbx-conf", "/etc/freepbx.conf", "Set path to freepbx db connection config file")
+}
+
+func main() {
+  var err error
+
+  flag.Parse()
+
+  sepTeplateFile, err = ioutil.ReadFile(sepTeplateFilePath)
+  if err != nil {
+    fmt.Fprintf(os.Stderr, "%v\n", err)
+    os.Exit(1)
+  }
+
+  //Getting params for db connection
+  dbConnParams, err := getDBConnectionParams()
+  if err != nil {
+    fmt.Fprintf(os.Stderr, "%v\n", err)
+    os.Exit(1)
+  }
+
+  //Connicting to db
+  db, err = sql.Open("mysql", dbConnParams)
+  if err != nil {
+    fmt.Fprintf(os.Stderr, "Error on initializing database connection: %s\n", err)
+    os.Exit(1)
+  }
+
+  //Checking db connection
+  err = db.Ping()
+  if err != nil {
+    fmt.Fprintf(os.Stderr, "Error on database connection: %s\n", err)
+    os.Exit(1)
+  }
+
+  db.SetMaxIdleConns(10)
+
+  fmt.Println("Starting freepbx tftp server")
+
+  s := tftp.NewServer(readHandler, nil)
+  err = s.ListenAndServe(":69")
+  if err != nil {
+    fmt.Fprintf(os.Stderr, "server: %v\n", err)
+    os.Exit(1)
+  }
+}